2008-10-30 9 views
8

Tengo el siguiente código heredado:Refactoring método estático/campo estático para las pruebas

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 una J2EE-Container.

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

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

Se permite el acceso a LegacyDataSource (la prueba no tiene por qué ser una prueba de unidad "pura").

EDITAR: No se permite introducir marcos de tiempo de ejecución adicionales.

+0

Actualicé mi respuesta según su nueva restricción. De hecho, tenemos un sistema que tuvo que resolver el mismo problema. – Robin

Respuesta

7

sólo para hacer @ sugerencia de un patrón de estrategia más concreta de Robin: (Tenga en cuenta 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 unitaria. No soy un gran fanático de tener que hacer este mantenimiento de configuración/desmontaje, pero ese es un desafortunado efecto secundario de tener una API basada en métodos estáticos (o Singletons para el caso). Lo que yo do me gusta acerca de esta prueba es que no usa JNDI - eso es bueno porque (a) funcionará rápido, y (b) la unidad de prueba solo debería probar la lógica comercial en el método doSomeLegacyStuff() de todos modos, no probando 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 
    } 
    } 
} 
2

Refactorice 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 objetos de recursos y estrategias en tiempo de ejecución.

En este caso, puede inyectar su fuente de datos.

EDITAR: Según su nueva restricción, puede lograr lo mismo utilizando un patrón de estrategia para establecer su fuente de datos en tiempo de ejecución. Probablemente pueda simplemente usar un archivo de propiedades para distinguir qué estrategia crear y suministrar el origen de datos. Esto no requeriría un nuevo marco, simplemente estarías codificando a mano la misma funcionalidad básica. Usamos esta idea exacta con un ServiceLocator para suministrar un origen de datos simulado cuando realizamos pruebas fuera del contenedor Java EE.

1

creo que la mejor solución a este problema se une que JNDI para un Código

El legado local utiliza la jndiName así:

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

Por lo tanto, la solución que aquí se unen un local (o todo lo que tienes para probar los datos) en un JNDI así:

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 entonces la unión:

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

O algo similar, espero que te ayude.

¡Buena suerte!