Jeśli powinieneś mieć tylko jedno stwierdzenie na test; jak przetestować wiele wejść?

15

Próbuję zbudować kilka przypadków testowych i przeczytałem, że powinieneś spróbować ograniczyć liczbę asercji na przypadek testowy.

Więc moje pytanie brzmi: jaki jest najlepszy sposób na przetestowanie funkcji z wieloma wejściami. Na przykład mam funkcję, która analizuje ciąg znaków od użytkownika i zwraca liczbę minut. Ciąg może mieć postać "5w6h2d1m", w której w, h, d, modpowiada liczbie tygodni, godzin, dni i minut.

Gdybym chciał zastosować się do zasady „1 asercja na regułę testu”, musiałbym wykonać wiele testów dla każdej odmiany danych wejściowych? To wydaje się głupie, więc zamiast tego mam coś takiego:

self.assertEqual(parse_date('5m'), 5)
self.assertEqual(parse_date('5h'), 300)
self.assertEqual(parse_date('5d') ,7200)
self.assertEqual(parse_date('1d4h20m'), 1700)

W jednym przypadku testowym. Czy jest lepszy sposób?

speg
źródło
Najlepszym sposobem na to jest użycie parametrów (niektóre frameworki obsługują tę funkcję, wszystkie frameworki powinny). W ten sposób
testujesz

Odpowiedzi:

23

Bardziej pragmatycznym sposobem spojrzenia na „zasadę” jednego twierdzenia na test, jest zapewnienie, że twoje twierdzenia w jednym teście obejmują jedną koncepcję.

W ten sposób nadal testujesz jedną koncepcję w jednym teście. W twoim przypadku, czy łańcuch wejściowy jest poprawnie parsowany w jednej dacie.

Powinieneś osądzać każdy przypadek z osobna, aby sprawdzić, czy lepiej jest przetestować jedną koncepcję z wieloma twierdzeniami, czy też mieć jedno twierdzenie w wielu testach.

Wybierz opcję, która zapewnia bardziej przejrzyste testy, mniej powtórzeń, a jednocześnie pozwala na podkreślenie różnych punktów błędów w metodzie. Chcesz, aby było jasne, gdy test nie powiedzie się dokładnie, co się stało, zamiast debugować test, aby dowiedzieć się, co poszło nie tak.

Gilles
źródło
17

Zależy to bardzo od biblioteki testowej. W bibliotece C # NUnit możesz zrobić coś takiego:

[TestCase('5m', 5)]
[TestCase('5h', 300)]
[TestCase('5d', 7200)]
[TestCase('1d4h20m', 1700)]
public void ParseDateTest(inputString, expectedMinutes)
{
    Assert.That(parse_date(inputString), Is.EqualTo(expectedMinutes));
}
Scroog1
źródło
W java z testowaniem masz metody
@DataProvider
to najlepsze rozwiązanie IMHO. w prawie każdym języku można sparametryzować swoje testy. dla java: @Parameterized , JunitParams , Zohhak
piotrek
3

Tak, wykonaj wiele testów dla każdej odmiany danych wejściowych.

Głównym celem jednego twierdzenia według wytycznych testowych jest (idealnie), aby jeden błąd prowadził do jednego niepowodzenia testu i odwrotnie, abyś dokładnie wiedział, co się nie udało. Następnie możesz pracować z jednym bardzo precyzyjnym testem w celu debugowania podstawowej przyczyny i weryfikacji. Możesz to zepsuć jednym twierdzeniem i możesz być w porządku z wieloma twierdzeniami. W tym konkretnym scenariuszu miałbym test dla każdego sufiksu i kilka kombinacji kolejności.

Mamy nadzieję, że jasne jest, dlaczego izolowanie testów jest zaletą: tracisz mniej czasu na debugowanie, gdy coś pójdzie nie tak. Teraz, jeśli naprawdę masz pewność, że test prawdopodobnie się nie powiedzie, a narzut związany z polowaniem na nim jest niewielki, być może warto po prostu przetestować je wszystkie naraz, aby zaoszczędzić czas na wdrożenie.

Historia pokazała jednak, że oszczędność czasu na pisanie kodu kosztem czytania / używania kodu nigdy nie jest tego warta. Stąd wytyczna.

Telastyn
źródło
1

Puryści twierdzą, że twierdzenia dotyczące różnych wartości danych wejściowych należy umieścić w oddzielnych metodach testowych w klasie testowej. Jednym z powodów jest to, że w zależności od interfejsu testowego często łatwiej jest odróżnić poszczególne niepowodzenia testu niż poszczególne stwierdzenia, co może doprowadzić do szybszej identyfikacji źródła niepowodzenia.

Podczas testowania za pomocą JUnit możemy obejść ten problem, używając wersji metod assert * z początkowym argumentem String, aby odróżnić jedno potwierdzenie od drugiego w ramach tej samej metody testowej.

self.assertEqual("just minutes", parse_date('5m'), 5)
self.assertEqual("just hours", parse_date('5h'), 300)
self.assertEqual("just days", ('5d') ,7200)
self.assertEqual("days, hours, minutes", parse_date('1d4h20m'), 1700)
Mike Partridge
źródło