2008-10-30 22 views
8

Mam następujący kod starszego typu:Refactoring statyczna metoda/pole statyczne Badań

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

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

Klasa ta działa w J2EE pojemnika.

Teraz chciałbym przetestować klasę poza kontenerem.

Jaka jest najlepsza strategia? Refaktoryzacja jest zasadniczo dozwolona.

Dostęp do LegacyDataSource jest dozwolony (test nie musi być "czystym" testem jednostkowym).

EDYCJA: Wprowadzenie dodatkowych ramek środowiska wykonawczego jest niedozwolone.

+0

Zaktualizowałem swoją odpowiedź na podstawie Twojego nowego ograniczenia. W rzeczywistości mamy system, który musiał rozwiązać ten sam problem. – Robin

Odpowiedz

7

Wystarczy dokonać @ sugestią Robin wzoru strategii bardziej konkretny: (Zauważ, że API publiczna oryginalnego pytanie pozostaje bez zmian).

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

... i testy JUnit. Nie jestem wielkim fanem konieczności wykonywania tej instalacji/zagospodarowania, ale jest to niefortunny efekt uboczny posiadania API opartego na statycznych metodach (lub Singletonach, jeśli o to chodzi). Co do tego testu, to nie korzysta z JNDI - to dobrze, ponieważ (a) będzie działać szybko, (b) test jednostkowy powinien testować logikę biznesową w metodzie doSomeLegacyStuff(), a nie testowanie rzeczywistego źródła danych. (Nawiasem mówiąc, zakłada to, że klasa testowa jest w tym samym pakiecie co 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

Dokonaj refaktoryzacji kodu, aby użyć iniekcji zależności. Następnie użyj preferowanej struktury DI (Spring, Guice, ...), aby wstrzyknąć swoje zasoby. Ułatwi to przełączanie się między obiektami zasobów i strategiami w środowisku wykonawczym.

W tym przypadku można wstrzyknąć źródło danych.

EDYCJA: W oparciu o nowe ograniczenie można wykonać to samo, używając wzoru strategii, aby ustawić źródło danych w czasie wykonywania. Prawdopodobnie wystarczy użyć pliku właściwości, aby rozróżnić strategię tworzenia i dostarczania źródła danych. Nie wymagałoby to nowych ram, po prostu kodowałbyś tę samą podstawową funkcjonalność. Użyliśmy tego pomysłu z ServiceLocatorem, aby dostarczyć pozorne źródło danych podczas testowania poza kontenerem Java EE.

1

Myślę, że najlepszym rozwiązaniem jest tu powiązać tego JNDI do lokalnego kodeksu

spuścizny używa jndiName tak:

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

Więc Rozwiązaniem jest tu wiązać lokalny (lub co masz do testowania danych) do JNDI tak:

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

a potem Oprawa:

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

Albo coś podobnego, nadzieja, która ci pomaga.

Powodzenia!