2008-10-30 7 views
8

Ich habe folgenden Legacy-Code:Refactoring statische Methode/statisches Feld zum Testen

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

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

Diese Klasse in einem J2EE-Container arbeitet.

Jetzt möchte ich die Klasse außerhalb des Containers testen.

Was ist die beste Strategie? Refactoring ist grundsätzlich erlaubt.

Der Zugriff auf die LegacyDataSource ist erlaubt (der Test muss kein "reiner" Unit-Test sein).

EDIT: Die Einführung zusätzlicher Laufzeit-Frameworks ist nicht erlaubt.

+0

ich meine Antwort aktualisiert, basierend auf Ihre neue Einschränkung. Wir haben tatsächlich ein System, das das gleiche Problem lösen musste. – Robin

Antwort

7

Nur um @ Robin Vorschlag einer Strategie Muster konkreter: (Beachten Sie, dass die öffentliche API von Ihrer ursprünglichen Frage bleibt unverändert.)

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 
    } 
} 

... und JUnit-Test. Ich bin kein großer Fan von dieser Setup/Teardown Wartung, aber das ist ein unglücklicher Nebeneffekt einer API basierend auf statischen Methoden (oder Singletons für diese Angelegenheit). Was ich tun wie über diesen Test ist es nicht JNDI verwenden - das ist gut, weil (a) es wird schnell laufen, und (b) der Komponententest sollte nur die Geschäftslogik in doSomeLegacyStuff() -Methode sowieso nicht testen Testen der tatsächlichen Datenquelle. (By the way, diese übernehmen die Testklasse im selben Paket wie MyLegacyClass ist.)

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

Refaktorieren Sie den Code für die Abhängigkeitsinjektion. Verwenden Sie dann Ihr bevorzugtes DI-Framework (Spring, Guice, ...), um Ihre Ressourcen zu injizieren. Das erleichtert den Wechsel zwischen Ressourcenobjekten und Strategien zur Laufzeit.

In diesem Fall können Sie Ihre Datenquelle injizieren.

BEARBEITEN: Basierend auf Ihrer neuen Einschränkung können Sie dasselbe erreichen, indem Sie ein Strategie-Muster verwenden, um Ihre Datenquelle zur Laufzeit zu setzen. Sie können wahrscheinlich nur eine Eigenschaftendatei verwenden, um zu unterscheiden, welche Strategie erstellt und die Datenquelle bereitgestellt wird. Dies würde keinen neuen Rahmen erfordern, Sie würden nur die gleiche grundlegende Funktionalität von Hand codieren. Wir haben diese genaue Idee mit einem ServiceLocator verwendet, um eine Schein-Datenquelle beim Testen außerhalb des Java-EE-Containers bereitzustellen.

1

Ich denke, dass die beste Lösung hier binden, dass JNDI zu einem lokalen

Das Vermächtnis-Code wird die jndiName wie die Verwendung von:

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

So, hier die Lösung eine lokale binden (oder was auch immer Sie haben, um Daten zu testen) in eine JNDI wie folgt aus:

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

Und dann die Bindung:

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

Oder etwas ähnliches, hoffe das hilft dir.

Viel Glück!