Pregunta

Tengo el siguiente código heredado:

public class MyLegacyClass
{
    private static final String jndiName = "java:comp/env/jdbc/LegacyDataSource"

    public static SomeLegacyClass doSomeLegacyStuff(SomeOtherLegacyClass legacyObj)
    {
       // do stuff using jndiName
    }
}

Esta clase está trabajando en un contenedor J2EE.

Ahora me gustaría probar la clase fuera del contenedor.

¿Cuál es la mejor estrategia? La refactorización está básicamente permitida.

Se permite acceder a LegacyDataSource (la prueba no tiene que ser una "unidad de prueba pura").

EDITAR: no se permite la introducción de marcos de ejecución adicionales.

¿Fue útil?

Solución

Solo para hacer que la sugerencia de @ Robin de un patrón de estrategia sea más concreta: (Observe que la API pública de su pregunta original permanece sin cambios).

public class MyLegacyClass {

  private static Strategy strategy = new JNDIStrategy();

  public static SomeLegacyClass doSomeLegacyStuff(SomeOtherLegacyClass legacyObj) {
    // legacy logic
    SomeLegacyClass result = strategy.doSomeStuff(legacyObj);
    // more legacy logic
    return result;
  }

  static void setStrategy(Strategy strategy){
    MyLegacyClass.strategy = strategy;
  }

}

interface Strategy{
  public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj);
}

class JNDIStrategy implements Strategy {
  private static final String jndiName = "java:comp/env/jdbc/LegacyDataSource";

  public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj) {
    // do stuff using jndiName
  }
}

... y prueba de JUnit. No soy un gran fan de tener que hacer esta configuración / mantenimiento de desmontaje, pero ese es un efecto secundario desafortunado de tener una API basada en métodos estáticos (o Singletons para el caso). Lo que hago me gusta de esta prueba es que no utiliza JNDI. Eso es bueno porque (a) se ejecutará rápido, y (b) la prueba unitaria solo debe probar la lógica de negocios en doSomeLegacyStuff ( ) método de todos modos, no prueba la fuente de datos real. (Por cierto, esto supone que la clase de prueba está en el mismo paquete que MyLegacyClass).

public class MyLegacyClassTest extends TestCase {

  private MockStrategy mockStrategy = new MockStrategy();

  protected void setUp() throws Exception {
    MyLegacyClass.setStrategy(mockStrategy);
  }

  protected void tearDown() throws Exception {
    // TODO, reset original strategy on MyLegacyClass...
  }

  public void testDoSomeLegacyStuff() {
    MyLegacyClass.doSomeLegacyStuff(..);
    assertTrue(..);
  }

  static class MockStrategy implements Strategy{

    public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj) {
      // mock behavior however you want, record state however
      // you'd like for test asserts.  Good frameworks like Mockito exist
      // to help create mocks
    }
  }
}

Otros consejos

Refactoriza el código para usar la inyección de dependencia. Luego use su marco DI preferido (Spring, Guice, ...) para inyectar sus recursos. Eso facilitará el cambio entre los objetos de recursos y las estrategias en tiempo de ejecución.

En este caso, puede inyectar su fuente de datos.

EDITAR: basado en su nueva restricción, puede lograr lo mismo usando un patrón de estrategia para configurar su fuente de datos en tiempo de ejecución. Probablemente solo puede usar un archivo de propiedades para distinguir qué estrategia crear y suministrar la fuente de datos. Esto no requeriría un nuevo marco, simplemente estaría codificando manualmente la misma funcionalidad básica. Utilizamos esta idea exacta con un ServiceLocator para proporcionar una fuente de datos simulada al realizar pruebas fuera del contenedor de Java EE.

Creo que la mejor solución aquí es vincular ese JNDI a un local

El código heredado está utilizando el jndiName de esa manera:

DataSource datasource = (DataSource)initialContext.lookup(DATASOURCE_CONTEXT);

Por lo tanto, la solución aquí es unir un local (o lo que sea que tenga para los datos de prueba) en un JNDI como ese:

  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName(System.getProperty("driverClassName"));
  dataSource.setUser("username");
  dataSource.setPassword("password");
  dataSource.setServerName("localhost");
  dataSource.setPort(3306);
  dataSource.setDatabaseName("databasename");

Y luego el enlace:

Context context = new InitialContext();
context.bind("java:comp/env/jdbc/LegacyDataSource",datasource); 

O algo similar, espero que te ayude.

¡Buena suerte!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top