2013-03-01 19 views
9

Mam klasę testu Parametrized z pakietem testów jednostkowych, które ogólnie kontrolują tworzenie niestandardowych wiadomości e-mail. W tej chwili klasa ma wiele testów, które zależą od czynników używanych w parametryzowanej klasie, przepływ testów jest taki sam dla każdego testu. Przykład testu:Jeśli instrukcje w testach

@Test 
public void testRecipientsCount() { 
    assertEquals(3, recipientsCount); 
} 

musiałem dodać dodatkową funkcjonalnością do mojej klasy e-mail, który dodaje kilka dodatkowych e-maili wewnętrznych do listy odbiorców, a to zdarza się tylko do niektórych przypadkach, a to prowadzi do mojego problemu .

Powiedzmy, że chcę potwierdzić ilość utworzonych wiadomości. Dla starego testu było to samo dla każdego przypadku, ale teraz różni się w zależności od przypadku. Najbardziej intuicyjny sposób dla mnie było, aby dodać if:

@Test 
public void testRecipientsCount(){ 
    if(something) { 
     assertEquals(3, recipientsCount); 
    } 
    else { 
     assertEquals(4, recipientsCount); 
    } 
} 
... 

ale my bardziej doświadczony współpracownik mówi, że powinniśmy unikać IFS w klasach testowych (i trochę się z tego).

Pomyślałem, że test podziału na dwie klasy testowe może zadziałać, ale doprowadziłoby to do redundancji kodu w obu klasach (wciąż muszę sprawdzić, czy zostały utworzone nie-iternalne wiadomości, ich rozmiar, zawartość itp.) Oraz dodano kilka linii dla jednego z nich.

Moje pytanie brzmi: jak to zrobić, więc nie używam, jeśli jest lub mnóstwo redundantnego kodu (nie używanie sparametryzowanej klasy spowodowałoby jeszcze więcej zbędnego kodu)?

+0

'dodaje kilka dodatkowych wewnętrzne wiadomości e-mail do listy odbiorców ". Czy ta lista adresów wewnętrznych jest wstrzykiwana do klasy? Jeśli tak, po prostu wyczyścisz tę listę dla większości testów i masz jeden lub dwa testy 'testInternalMail', które wypełniają tę listę –

+0

Nie sądzę, że jest to możliwe w klasie testowej Parametrized. Dodano przypadki testowe, które sugerowałbyś uruchomić dla każdego zestawu argumentów, a asercje byłyby inne dla niektórych. Twoje rozwiązanie działałoby bez klasy Parametrized, ale chciałbym tego uniknąć. – Raidmaster

+0

Czy możesz zmienić swój opublikowany kod na bardziej kompletną (ale wciąż niewielką i fałszywą) klasę testową, abyśmy mogli zrozumieć, jak konfigurujesz swoje parametry? –

Odpowiedz

3

Moim zdaniem Junit należy czytać jak protokół. Oznacza to, że można napisać nadmiarowy kod, aby zwiększyć czytelność teczki testowej. Napisz do testcase dla każdego możliwego oświadczenia if w logice biznesowej, a także negatywnych przypadkach. To jedyny sposób na uzyskanie 100% pokrycia testowego. używam strukturę:

- testdata preparation 
- executing logic 
- check results 
- clear data 

Ponadto należy napisać kompleks twierdzi dużych obiektów własnych klas abstrakcyjnych:

abstract class YourBusinessObjectAssert{ 
    public static void assertYourBussinessObjectIsValid(YourBusinessObject pYourBusinessObject,  Collection<YourBusinessObject> pAllYourBusinessObject) { 
for (YourBusinessObject lYourBusinessObject : pAllYourBusinessObject) { 
    if (lYourBusinessObject.isTechnicalEqual(pYourBusinessObject)) { 
    return; 
    } 
} 
assertFalse("Could not find requested YourBusinessObject in List<YourBusinessObject>!", true); 
} 
} 

będzie zmniejszyć złożoność kodu i jesteś udostępnianie inni programiści.

0

Test jednostkowy powinien, moim zdaniem, przetestować tylko jedną rzecz, jeśli to możliwe. Jako takie chciałbym powiedzieć, że jeśli potrzebujesz instrukcji if, prawdopodobnie potrzebujesz więcej niż jeden test jednostkowy - po jednym dla każdego bloku w kodzie if/else.

Jeśli to możliwe Powiedziałbym test powinien przeczytać jak historia - mój preferowany układ (i jej nie mój pomysł :-) - jego dość powszechnie stosowany) to:

- given: do setup etc 
- when: the place you actually execute/call the thing under test 
- expect: verify the result 

Inną zaletą urządzenia Testowanie testowe Jedną z rzeczy jest to, że gdy wystąpi awaria, jej jednoznaczna przyczyna - jeśli masz długi test z wieloma możliwymi wynikami, znacznie trudniej jest uzasadnić, dlaczego test się nie powiódł.

0

Dlaczego nie ma prywatnej metody testowania rzeczy, które są wspólne dla każdej metody? Coś podobnego (ale prawdopodobnie z jakiegoś parametru wejściowego dla metody testCommonStuff()):

@Test 
public void testRecipientsCountA(){ 
    testCommonStuff(); 
    // Assert stuff for test A 
} 

@Test 
public void testRecipientsCountB(){ 
    testCommonStuff(); 
    // Assert stuff for test B 
} 

private void testCommonStuff() { 
    // Assert common stuff 
} 

ten sposób nie dostaniesz kod nadmiarowy i można podzielić na mniejsze testu testów. Również sprawisz, że twoje testy będą mniej podatne na błędy, JEŚLI rzeczywiście powinny testować te same rzeczy.Nadal będziesz wiedzieć, który test się nie powiódł, więc identyfikowalność nie powinna stanowić problemu.

+0

Używam klasy Parametrized i uruchomiłbym obie twoje przypadki testowe dla każdego zestawu argumentów. Prawidłowy sposób polega na uruchomieniu tylko jednego, w zależności od faktycznego argumentu. – Raidmaster

+0

@ Badman6 Nie w pełni rozumiem. Mówisz "ale to doprowadziłoby do zbędnego kodu w obu klasach (wciąż muszę sprawdzić, czy zostały utworzone nie-nowe wiadomości, ich rozmiar, treść itd.)". Czy to nie oznacza, że ​​masz kod rezerwowy w jednej lub wielu metodach testowych? Jeśli tak, możesz to zrobić we własnej prywatnej metodzie, jak wyżej, i wywołać ją z każdej z metod, które powinny ją uruchomić. – Magnilex

+0

To nie to. Liczba odbiorców zależy od parametru podanego w argumencie provider dla klasy parametrów. Jeśli będę chciał zachować kod nadmiarowy klasy Parametrized, będzie to wynikiem podziału moich testów na 2 klasy (jedną dla przypadków z, bez odbiorcy wewnętrznego). Dla testów klasy dzielenie kodu nie jest tak naprawdę opcją. Twoje rozwiązanie jest w porządku, po prostu nie dla klasy Parametrized – Raidmaster

0

Nie jestem pewien, czy możliwe jest czyszczenie tego, czego oczekujesz, w sparametryzowanym teście. Jeśli potrzebujesz różnych zachowań testowych w zależności od tego, który parametr dotyczy niektórych funkcji, możesz po prostu lepiej testować te funkcje osobno - w różnych klasach testowych, które nie są parametryzowane.

Jeśli naprawdę chcesz zachować wszystko w parametryzowane klas testowych, byłbym skłonny uczynić funkcję pomocnika, tak aby Twój przykład testu przynajmniej brzmi prostego twierdzenia:

@Test 
public void testRecipientsCount(){ 
    assertEquals(expectedCount(something), recipientsCount) 
} 

private int expectedCount(boolean which) { 
    if (something){ 
     return 3; 
    } 
    else { 
     return 4; 
    } 
} 
Powiązane problemy