2009-08-18 15 views
19

Próbuję wdrożyć testowanie jednostkowe dla aproject, używa on starszego projektu "utility", który jest zaśmiecony statycznymi metodami i wiele klas jest ostatecznych lub ich metody są ostateczne. W ogóle nie jestem w stanie zaktualizować starszego projektu.Jak mogę przetestować końcowe i statyczne metody projektu narzędzia?

JMock i EasyMock dławią się na ostatnich metodach i nie widzę dobrego sposobu na przetestowanie wywołań statycznych. Jakie są techniki, aby je przetestować?

+0

Czy jest jakikolwiek powód, dla którego konieczne jest użycie makiety Framework? –

+0

Tak, wywołania metod używają właściwości JNDI do łączenia się z bazą danych i JMS, nie chcę implementować wszystkich elementów do mojego testu. –

+0

Czy jesteś w stanie zinwentaryzować któryś ze starszych kodów, aby Ci pomóc? –

Odpowiedz

21

Jeśli jesteś w stanie byłaby kod, można owinąć połączeń na końcowych/metody statyczne w prostych metod instancji, na przykład:

protected Foo doBar(String name) { 
    return Utility.doBar(name); 
} 

Pozwala to zastąpić metodę otoki w test jednostkowy, aby zwrócić fałszywą instancję Foo.

Alternatywnie można użyć Powermock, która rozciąga EasyMock (i), aby pozwolić Mockito szyderczy metod końcowych i statycznych:

PowerMock to ramy, które rozciągają innych bibliotek makiety takich jak EasyMock z bardziej potężnymi możliwościami. PowerMock wykorzystuje niestandardową manipulację klockami i kodami bajtowymi, aby umożliwić wyszydzanie statycznych metod, konstruktorów, końcowych klas i metod, prywatnych metod, usuwania inicjalizatorów statycznych i innych.

Oto test example szyderczy statyczną metodę ostateczną, przykład pokazuje, jak wyśmiewać jakieś inne rodzaje też:

@Test 
public void testMockStaticFinal() throws Exception { 
    mockStatic(StaticService.class); 
    String expected = "Hello altered World"; 
    expect(StaticService.sayFinal("hello")).andReturn("Hello altered World"); 
    replay(StaticService.class); 

    String actual = StaticService.sayFinal("hello"); 

    verify(StaticService.class); 
    assertEquals("Expected and actual did not match", expected, actual); 

    // Singleton still be mocked by now. 
    try { 
     StaticService.sayFinal("world"); 
      fail("Should throw AssertionError!"); 
    } catch (AssertionError e) { 
     assertEquals("\n Unexpected method call sayFinal(\"world\"):", 
      e.getMessage()); 
    } 
} 
+0

+1 dla PowerMock, ponieważ refaktoryzacja nie wchodzi w grę. –

+0

to wygląda na to, czego potrzebuję, bo wiesz, czy działa w kompilacji Mavena? –

+0

Nie próbowałem jeszcze, ale wygląda na to: http://code.google.com/p/powermock/wiki/EasyMock_maven –

5

Jak o poziomie zadnie/Dependency Injection?

Ponieważ starszym projektem użytkowym jest Twoja zależność, utwórz interfejs, aby oddzielić go od kodu. Teraz twoja realna/produkcyjna implementacja tego interfejsu deleguje do starszych metod użytkowych.

public LegacyActions : ILegacyActions 
{ 
    public void SomeMethod() { // delegates to final/static legacy utility method } 
} 

Do testów można utworzyć próbę tego interfejsu i unikać interakcji ze starszym narzędziem.

1

Jeśli twoja metoda bez możliwości refaktoryzacji używa czegoś takiego jak JNDI do łączenia się z inną usługą, powinienem rozważyć uruchomienie usługi JDNI i zapełnienie jej stosowanymi przez ciebie kodami. To ból, ale stosunkowo prosty. Może to oznaczać utworzenie bazy danych lub detektora JMS lub czegoś podobnego, ale powinna istnieć lekka implementacja java, którą można wpuścić do testów.

+0

To dobrze, jeśli chodzi o testy integracyjne, ale testy jednostkowe nie powinny wymagać bazy danych. –

+0

Możliwe, że będziesz w stanie sfałszować bazę danych i wprowadzić tę sztuczkę do JNDI. – extraneon

+1

@Rich Sprzedawca: Zgadzam się, ale jeśli masz jajka, możesz zjeść jajka na bekonie, jeśli masz trochę boczku. Mam przez to na myśli to, że oryginalne pytanie stwierdziło, że nie może refaktoryzować kodu i chce dodać testy. Czasami musisz pracować z tym, co masz, a nie co chcesz. –

3

JMockit Pozwala na symulowanie metod statycznych i klas końcowych. Zakładam, że używa jakiegoś classloadin-fu, chociaż tak naprawdę nie przyjrzałem się temu.

Oczekiwania JMockit API pozwala oczekiwania należy ustawić na każdym rodzaju wywołania metody (na interfejsach, klasy abstrakcyjne, beton końcowe lub spoza klasy końcowe, a na statycznych metod), jak również na klasy tworzenie wystąpień za pomocą dowolnych konstruktorów .

1

Jak już wskazano, można użyć JMockit. Przykład:

@Test 
public void mockStaticAndFinalMethods(final LegacyService mock) 
{ 
    new NonStrictExpectations() 
    { 
     { 
     LegacyService.staticMethod("hello"); result = "Hello altered World"; 
     } 
    }; 

    String actual = LegacyService.staticMethod("hello"); 
    new LegacyService().finalMethod(123, "test"); 

    assertEquals("Hello altered World", actual); 

    new Verifications() 
    { 
     { 
     mock.finalMethod(123, "test"); // verify this call occurred at least once 
     } 
    }; 
} 

Strona domowa projektu JMockit zawiera porównanie z PowerMock, dla zainteresowanych.

-1

JMock wraz z JDave mogą wykpić ostateczne metody i klasy, jeśli trzeba. Here są instrukcjami. Powiedziałbym, że będę traktował ten kod źródłowy (jak już sugerowali inni) jako zewnętrzną zależność i buduje interfejsy i kpi z nich. Jest to kolejna warstwa pośrednia, ale ponieważ nie można zmienić tego starszego kodu, wydaje się to rozsądne.