2012-06-18 10 views
8

Chcę uruchomić te same testy JUnit dla różnych implementacji interfejsu. Znalazłem ładne rozwiązanie z opcji @Parameter:Testowanie implementacji wielu interfejsów z tymi samymi testami - JUnit4

public class InterfaceTest{ 

    MyInterface interface; 

    public InterfaceTest(MyInterface interface) { 
    this.interface = interface; 
    } 

    @Parameters 
    public static Collection<Object[]> getParameters() 
    { 
    return Arrays.asList(new Object[][] { 
     { new GoodInterfaceImpl() }, 
     { new AnotherInterfaceImpl() } 
    }); 
    } 
} 

Ten test będzie prowadzony dwukrotnie, najpierw z GoodInterfaceImpl następnie z AnotherInterfaceImpl klasie. Ale problem polega na tym, że większość testcases potrzebuje nowego obiektu. Uproszczony przykład:

@Test 
public void isEmptyTest(){ 
    assertTrue(interface.isEmpty()); 
} 

@Test 
public void insertTest(){ 
    interface.insert(new Object()); 
    assertFalse(interface.isEmpty()); 
} 

Jeśli isEmptyTest prowadzony jest po insertTest zawiedzie.

Czy istnieje opcja automatycznego uruchamiania każdej wersji testowej z nową instancją implementacji?

BTW: Wdrożenie clear() lub reset() -method dla interfejsu nie jest tak naprawdę opcje ponieważ nie musiałby go w kodzie produkcyjnym.

+0

Proponuję patrząc wstrzykiwania zależności. Oto mały artykuł na temat tego, jak wykorzystać go do osiągnięcia dokładnie tego, czego szukasz http://www.javaranch.com/journal/200709/dependency-injection-unit-testing.html –

+0

Dziękuję za szybką odpowiedź ! W tym artykule opisano sposób dzielenia zależności w interfejsie. Chcę przetestować interfejs (a nie klasę, która używa interfejsu lub mówić w kategoriach artykułu: chcę przetestować farmę zdalną [odpowiednio silniki], a nie serwlet farmy [odpowiednio samochody]). –

Odpowiedz

4

Utwórz interfejs fabryczny i implementacje, prawdopodobnie tylko w hierarchii testów, jeśli nie potrzebujesz czegoś takiego w produkcji, i zwróć listę fabryk na numer getParameters().

Następnie można wywołać fabrykę w metodzie adnotowanej @Before, aby uzyskać nową instancję testowanej klasy dla każdego uruchomienia metody testowej.

+0

Wielkie dzięki! To sprytne rozwiązanie, zaimplementuję to :) –

5

Oto inne podejście do wzorca Template Method:

Testy interfejsu zorientowanych iść do klasy bazowej:

public abstract class MyInterfaceTest { 

    private MyInterface myInterface; 

    protected abstract MyInterface makeContractSubject(); 

    @Before 
    public void setUp() { 
     myInterface = makeContractSubject(); 
    } 

    @Test 
    public void isEmptyTest(){ 
     assertTrue(myInterface.isEmpty()); 
    } 

    @Test 
    public void insertTest(){ 
     myInterface.insert(new Object()); 
     assertFalse(myInterface.isEmpty()); 
    } 
} 

Dla każdej klasy betonu, należy zdefiniować konkretną klasę testową:

public class GoodInterfaceImplTest extends MyInterfaceTest { 

    @Override 
    protected MyInterface makeContractSubject() { 
     // initialize new GoodInterfaceImpl 
     // insert proper stubs 
     return ...; 
    } 

    @Test 
    public void additionalImplementationSpecificStuff() { 
     ... 
    } 
} 

Niewielka przewaga nad @Parameter polega na tym, że dostajesz nazwę konkretnej klasy testowej zgłaszanej, gdy test się nie powiedzie, więc od razu wiesz, która implementacja się nie powiodła.

Przy okazji, aby to podejście w ogóle działało, interfejs musi być zaprojektowany w sposób umożliwiający testowanie wyłącznie za pomocą metod interfejsu. Oznacza to testowanie oparte na stanie - nie można weryfikować prób w podstawowej klasie testowej. Jeśli musisz zweryfikować makiety w testach specyficznych dla implementacji, testy te muszą przejść do konkretnych klas testowych.

+0

+1 za kolejne dobre podejście :) Ale jeśli chodzi o nazwy klas, jest teraz możliwe (JUnit> = 4.11), aby nadać różne nazwy testom, choć nie jest to bardzo przydatne używać -> http://stackoverflow.com/questions/650894/changing-names-of-parameterized-tests (s. najwyżej oceniona odpowiedź, nie akceptowana). Mimo to lubię twoje rozwiązanie, ponieważ możesz dodać testy specyficzne dla implementacji do pochodnych klas testowych. W przeciwnym razie potrzebuję dodatkowej klasy. –

1

wszelki wypadek ktoś osiąga tutaj (jak ja), patrząc na testowanie wielu implementacji tego samego interfejsu w .NET można zobaczyć jedną z metod, które używałem w jednym z projektów here

Poniżej jest to, co śledzimy w skrócie:

Ta sama dll projektu testowego jest uruchamiana dwukrotnie przy użyciu vstest.console, poprzez ustawienie zmiennej środowiskowej. Wewnątrz testu (w inicjalizacji zespołu lub inicjalizacji testu) zarejestruj odpowiednie implementacje w kontenerze IoC, w oparciu o wartość zmiennej środowiskowej.

0

W JUnit 5 można zrobić:

@ParameterizedTest 
@MethodSource("myInterfaceProvider") 
void test(MyInterface myInterface) {} 

static Stream<MyInterface> myInterfaceProvider() { 
    return Stream.of(new ImplA(), new ImplB()); 
} 

interface MyInterface {} 

static class ImplA implements MyInterface {} 

static class ImplB implements MyInterface {} 
Powiązane problemy