2011-08-02 14 views
11

Mam PersonDao, przeciwko któremu piszę testy jednostkowe.Wiele klas testowych lub jedna klasa testowa z wieloma metodami?

Istnieje około 18-20 metody PersonDao formularza -

getAllPersons() 
    getAllPersonsByCategory() 
    getAllPersonsUnder21() etc 

moje podejście do tego badania było stworzenie PersonDaoTest z około 18 metody badań testowanie każdej metody w PersonDao

Następnie utworzyłem test PersonDaoPagination, który testował te 18 metod, stosując parametry paginacji.

Czy jest to niezgodne z najlepszymi praktykami TDD? Powiedziano mi, że to powoduje zamieszanie i jest niezgodne z najlepszymi praktykami, ponieważ jest to niestandardowe. Sugerowano zamiast tego połączenie dwóch klas w PersonDaoTest.

Jak rozumiem, im więcej dzieli się na wiele klas, tym lepiej, proszę, skomentuj.

Odpowiedz

10

Fakt, że masz zestaw 18 testów, które będziesz musiał powielić, aby przetestować nową funkcję, to zapach, który sugeruje, że Twoja klasa PersonDao przejmuje wiele obowiązków. W szczególności wydaje się, że jest odpowiedzialny za wysyłanie zapytań/filtrowanie i dzielenie na strony. Możesz rzucić okiem na to, czy możesz wykonać trochę pracy projektowej, aby wyodrębnić funkcję dzielenia na strony na osobną klasę, która może być następnie przetestowana niezależnie.

Ale w odpowiedzi na twoje pytanie, jeśli okaże się, że masz klasę, która ma pozostać złożona, to całkiem dobrze jest używać wielu klas testowych jako sposobu organizowania dużej liczby testów. Odpowiedź @ Gishu na testy grupujące według ich konfiguracji to dobre podejście. @ Ryan w odpowiedzi na grupowanie według "aspektów" lub funkcji jest kolejnym dobrym podejściem.

+0

+1 za wskazanie, że PersonDao podejmowało wiele obowiązków. – Sam

2

Moje 2 centy: jeśli masz dużą klasę, która ma różne "fasety" do niej, takie jak paginacja, uważam, że często może to ułatwić bardziej zrozumiałe testy , a nie zapakować je wszystkie w jedną klasę. Nie mogę twierdzić, że jestem guru TDD, ale ćwiczę pierwszy test religijnie, że tak powiem. Nie robię tego często, ale nie jest to rzadkością, że napiszę więcej niż jedną klasę testową dla konkretnej klasy. Wiele osób zdaje się jednak zapominać o dobrych praktykach związanych z kodowaniem, takich jak rozdzielanie obaw podczas pisania testów. Nie jestem pewien dlaczego.

1

Myślę, że jedna klasa testowa na klasę jest w porządku - jeśli twoja implementacja ma wiele metod, twoja klasa testowa będzie miała wiele metod - wielka sprawa.

Można rozważyć kilka rzeczy jednak:

Twoje metody wydają się nieco „zbyt specyficzny” i mogłby abstrakcji albo uogólnienia, na przykład zamiast getAllPersonsUnder21() rozważyć getAllPersonsUnder(int age)

Jeżeli istnieją pewne więcej ogólne aspekty twojej klasy, rozważ testowanie ich za pomocą jakiegoś popularnego kodu testowego wykorzystującego oddzwanianie. Dla trywialny przykład do zilustrowania testowanie że zarówno getAllPersons() zwraca wiele hitów, to zrobić:

@Test 
public void testGetAllPersons() { 
    assertMultipleHits(new Callable<List<?>>() { 
     public List<?> call() throws Exception { 
      return myClass.getAllPersons(); // Your call back is here 
     } 
    });  
} 

public static void assertMultipleHits(Callable<List<?>> methodWrapper) throws Exception { 
    assertTrue("failure to get multiple items", methodWrapper.call().size() > 0); 
} 

Ta metoda statyczna może być używany przez każdą klasę aby sprawdzić, czy „jakiś sposób” zwraca wiele trafień. Można to rozszerzyć, aby wykonać wiele testów przez to samo wywołanie zwrotne, na przykład uruchamiając je z lub bez połączenia DB, sprawdzając, czy zachowuje się poprawnie w każdym przypadku.

+0

Oczywiście, moja implementacja dotyczy getAllPersonsByCategory (kategoria) . Czego nie rozumiem, to dlaczego powinienem mieć getAllPersonsByCategoryTest() i getAllPersonsByCategoryPaginationTest() i getAllPersonsByCategoryValidationTest() w tej samej klasie testu. Dlaczego nie można ich podzielić na dwie/trzy różne klasy?Ponieważ oryginalna klasa ma około 20 metod, mój Test może zakończyć się 40-60 metodami, bardzo kłopotliwymi do pracy z IMO. – Sam

+0

btw, przykład, który mam tutaj jest hipotetyczny, nie mam faktycznie getAllPersonsUnder21(). Przepraszam za zamieszanie. Podpowiedź to świetna sztuczka, dzięki ... – Sam

+1

Pod koniec dnia ważna jest klarowność. Można robić * cokolwiek *, o ile twoja praca jest przejrzysta i możliwa do utrzymania. – Bohemian

7

Nie można podać szerokiej odpowiedzi bez patrzenia na kod ... z wyjątkiem użycia tego, co wydaje się spójne dla ciebie i twojego zespołu.

Zauważyłem, że testy grupowania na podstawie ich konfiguracji działa ładnie w większości przypadków. np. jeśli 5 testów wymaga tego samego ustawienia, zwykle ładnie mieszczą się w urządzeniu testowym. jeśli szósty test wymaga innej konfiguracji (mniej lub więcej), podziel ją na osobny uchwyt testowy.

Prowadzi to również do testowania urządzeń spójnych pod względem funkcji (tj. Testów pogrupowanych w funkcję), spróbuj. Nie jestem świadomy żadnej najlepszej praktyki, która mówi, że musisz mieć jedną klasę testową na klasę produkcyjną ... w praktyce uważam, że mam n klas testowych na klasy produkcyjne, najlepszą praktyką byłoby używanie dobrych nazw i utrzymywanie powiązanych testów zamknij (w nazwanym folderze).

1

Pracuję nad automatyzacją testów aplikacji internetowej przy użyciu selenu. To nie jest testowanie jednostkowe, ale może się okazać, że obowiązują pewne zasady. Testy są bardzo złożone i ustaliliśmy, że jedynym sposobem na wdrożenie testów w sposób spełniający wszystkie nasze wymagania jest posiadanie 1 testu na klasę. Uważamy więc, że każda klasa jest testem indywidualnym, a następnie mogliśmy użyć metod jako różnych etapów testu. Na przykład:

public SignUpTest() 
{ 
    public SignUpTest(Map<String,Object> data){} 
    public void step_openSignUpPage(){} 
    public void step_fillForm(){} 
    public void step_submitForm(){} 
    public void step_verifySignUpWasSuccessfull(){} 
} 

Wszystkie kroki są zależne, podążają określoną kolejnością, a jeśli ktoś zawiedzie, pozostałe nie zostaną wykonane.

Oczywiście, każdy krok jest testem sam w sobie, ale wszystkie razem tworzą test wyrywkowy.

Wymagania było coś takiego:

  1. Badania muszą być dane napędzane, to jest, ten sam test wykonać równolegle z różnymi wejściami.
  2. Testy muszą być również uruchomione w różnych przeglądarkach jednocześnie. Zatem każdy test będzie działał równolegle równolegle "input_size x browserscount".
  3. Testy skupią się na obiegu pracy w sieci, na przykład "zarejestruj się z ważnymi danymi" i zostaną podzielone na mniejsze jednostki testowe dla każdego etapu przepływu pracy. Ułatwi to konserwację i debugowanie (jeśli wystąpi awaria, powie: SignUpTest.step_fillForm(), a od razu będzie wiadomo, co jest nie tak).
  4. Kroki testu mają takie same wejście testowe i stan (na przykład identyfikator utworzonego użytkownika). Wyobraź sobie, że można umieścić w tej samej klasie etapy różnych testów, na przykład:

    public SignUpTest() 
    {  
        public void signUpTest_step_openSignUpPage(){} 
        public void signUpTest_step_step_fillForm(){} 
        public void signUpTest_step_step_submitForm(){} 
        public void signUpTest_step_verifySignUpWasSuccessfull(){} 
    
        public void signUpNegativeTest_step_openSignUpPage(){} 
        public void signUpNegativeTest_step_step_fillFormWithInvalidData(){} 
        public void signUpNegativeTest_step_step_submitForm(){} 
        public void signUpNegativeTest_step_verifySignUpWasNotSuccessfull(){} 
    } 
    

    Potem, mając w tym samym stanie klasy należącego do testów 2 będzie bałagan.

Mam nadzieję, że było jasne i może się okazać, że jest to przydatne. Na koniec wybierz, co będzie reprezentować twój test: jeśli klasa lub metoda jest decyzją, która moim zdaniem będzie zależna od int: jaki jest cel testu (w moim przypadku przepływ pracy wokół funkcji), co jest łatwiejsze implementować i utrzymywać, jeśli test nie powiedzie się, jak sprawić, aby błąd był dokładniejszy i jak ułatwić debugowanie, co doprowadzi cię do bardziej czytelnego kodu, itp.

Powiązane problemy