2012-04-06 12 views
12

Wszystko,Jak fałszywych InitialContext z domyślnego konstruktora

Próbuję zrobić kilka testów jednostkowych w jakimś archaicznym kodu Java (nie interfejsy, nie abstrakcja, etc.)

Jest to aplet, który używa ServletContext (który, jak przypuszczam, jest skonfigurowany przez Tomcat) i zawiera informacje o bazie danych, jest skonfigurowany w pliku web.xml/context.xml. Teraz mam zorientowali się, jak zrobić Fałszywe ServletContext, ale kod ma

InitialContext _ic = new InitialContext(); 

wszędzie (tak, to nie jest możliwe, aby ją wymienić). Potrzebuję znaleźć sposób, aby domyślny InitialContext() był w stanie wykonać _ic.lookup(val) bez powodowania wyjątku.

Zakładam, że istnieje sposób, w jaki plik context.xml jest ładowany, ale jak działa ta magia, rysuję puste miejsce. Ktoś ma jakieś pomysły?

+0

To, że występuje bardzo często, nie oznacza, że ​​zastąpienie go jest zdecydowanie niewykonalne. Heck, nawet zmiana w celu użycia statycznej metody fabrycznej pozwoliłaby na * więcej * testowalności (chociaż wyraźnie nie jest tak ładna jak niektóre alternatywy). –

Odpowiedz

3

Możesz użyć PowerMock, aby sfałszować konstrukcję InitialContext i kontrolować jej zachowanie. Drwiny Konstruktora są udokumentowane here.

Testy PowerMock mogą być dość kłopotliwe i skomplikowane, refaktoryzacja jest zwykle lepszym rozwiązaniem.

+0

+1. PowerMock jest potężny, ale na pewno spowodował wystarczającą liczbę bólów głowy, że refaktoryzacja jest bardzo pożądana. – AHungerArtist

+0

Myślę, że to najlepsze rozwiązanie dla tego, co próbuję zrobić. dzięki! – Austin

+0

Unikaj PowerMock, chyba że nie masz już żadnej innej opcji ... Wiele artykułów wyjaśnia dlaczego. –

4

Spróbuj ustawić zmienne systemowe przed:

System.setProperty(Context.INITIAL_CONTEXT_FACTORY, 
     "org.apache.naming.java.javaURLContextFactory"); 
System.setProperty(Context.URL_PKG_PREFIXES, 
     "org.apache.naming"); 
InitialContext ic = new InitialContext(); 

Jeśli używasz JUnit, wykonaj ten dokument: https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit

+1

To daje 'java.lang.ClassNotFoundException: org.apache.naming.java.javaURLContextFactory' –

+1

ma być środowisko tomcat w projekcie. Trzeba dodać słoiki tomcat –

+0

Więc jeśli używam JBossa, myślę, że muszę używać środowiska JBoss. Dziękuję Ci! –

6

Oto moje rozwiązanie do konfigurowania Inintial kontekst dla moich testów jednostkowych. Pierwszy dodałem następujące zależności testowy do mojego projektu:

<dependency> 
    <groupId>org.apache.tomcat</groupId> 
    <artifactId>catalina</artifactId> 
    <version>6.0.33</version> 
    <scope>test</scope> 
</dependency> 

Potem stworzył metodę statyczną z następującego kodu:

public static void setupInitialContext() throws Exception { 
    System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory"); 
    System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming"); 
    InitialContext ic = new InitialContext(); 
    ic.createSubcontext("jdbc"); 
    PGSimpleDataSource ds = new PGSimpleDataSource(); 
    ds.setDatabaseName("postgres"); 
    ds.setUser("postgres"); 
    ds.setPassword("admin"); 
    ic.bind("jdbc/something", ds); 
} 

wreszcie każdy z mojej klasy testowej dodać metodę @BeforeClass który wywołuje setupInitialContext.

+0

w stosunku do tego https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit był bardzo dobry – hephestos

35

Wykorzystaj fakt, że InitialContext wykorzystuje SPI obsłużyć jej tworzenia. Możesz podłączyć się do jego cyklu życia, tworząc implementację javax.naming.spi.InitialContextFactory i przekazując ją do testów za pomocą właściwości systemu javax.naming.factory.initial (Context.INTITIAL_CONTEXT_FACTORY). To jest prostsze niż się wydaje.

Biorąc pod uwagę to klasa:

public class UseInitialContext { 

    public UseInitialContext() { 
     try { 
      InitialContext ic = new InitialContext(); 
      Object myObject = ic.lookup("myObject"); 
      System.out.println(myObject); 
     } catch (NamingException e) { 
      e.printStackTrace(); 
     } 
    } 


} 

I to impl od InitialContextFactory:

public class MyInitialContextFactory implements InitialContextFactory { 

    public Context getInitialContext(Hashtable<?, ?> arg0) 
      throws NamingException { 

     Context context = Mockito.mock(Context.class); 
     Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!"); 
     return context; 
    } 
} 

Tworzenie instancji UseInitialContext w teście junit z

-Djava.naming.initial.factory=initial.context.test.MyInitialContext 

na wyjściach wiersza poleceń This is my object!! (łatwe do skonfigurowania w Eclipse). Lubię Mockito dla kpiny i szyderstwa. Polecam również Micheal Feather's Working Effectively with Legacy Code, jeśli masz do czynienia z dużą ilością starego kodu. Chodzi o to, jak znaleźć szwy w programach, aby wyizolować konkretne elementy do testowania.

+0

Zamiast „java.naming.initial.factory”, myślę, że odpowiedni system Właściwość to "java.naming.factory.initial". Przynajmniej w java 6. Dzięki za post! – marciopd

+3

'java.naming.factory.initial' jest również dla java 7. –

+0

FWIW, napisałem bardzo podobną odpowiedź do bardzo podobnego pytania po pewnym czasie i zrobiłem to [używając JUnit 'TestTule' do obsługi instalacji i teardown] (http://stackoverflow.com/a/17083737/116639). –

1

Dziś mam w obliczu tego samego problemu (nie możemy użytkownik PowerMock) i rozwiązano to w ten sposób:

  1. Nie odnośnika w konstruktorze więc kiedy powoływać @InitMock na obiekcie, konstruktor nie wymaga jeszcze kontekstu.

  2. Tworzenie metodę pobierania fasoli usług, gdy są potrzebne jak "getService() serviceMethod (param, param ...).":

/* Class ApplicationResourceProvider */ 

    /* We can mock this and set it up with InjectMocks */ 
    InitialContext ic; 

    /* method hiding the lookup */ 
    protected ApplicationService getService() throws NamingException { 
     if(ic == null) 
      ic = new InitialContext(); 
     return (ApplicationService)ic.lookup("java:global/defaultApplicationLocal"); 
    } 
  1. w teście ustawić:
@Mock 
ApplicationService applicationServiceBean; 

@Mock 
InitialContext ic; 

@InjectMocks 
ApplicationResourceProvider arp; 

@Before 
public void setUp() throws Exception { 
    MockitoAnnotations.initMocks(this); 
    when(ic.lookup(anyString())).thenReturn(applicationServiceBean); 
    ... 
} 
Powiązane problemy