Jednostka przetestowała moją klasę. Jak zacząć test integracyjny?

19

Napisałem klasę, która zarządza adresatami na liście MailChimp, o nazwie MailChimpRecipient. Wykorzystuje klasę MCAPI, która jest zewnętrznym opakowaniem API.

http://apidocs.mailchimp.com/api/1.3/ http://apidocs.mailchimp.com/api/downloads/

Przekazuję obiekt MCAPI do konstruktora obiektu MailChimpRecipient, więc napisałem testy jednostkowe przy użyciu PHPUnit, które testują całą logikę we własnej klasie (nie testuję klasy MCAPI). Mam 100% pokrycia kodu i wszystkie testy są udane. Odbywa się to przez wyśmiewanie i usuwanie obiektu MCAPI.

Następnym krokiem było napisanie testu integracji, również przy użyciu PHPUnit, w którym zbudowałbym urządzenie MailChimpRecipient przy użyciu prawdziwego obiektu MCAPI, skonfigurowanego do używania prawdziwej listy MailChimp.

Napisałem coś, co moim zdaniem jest testem integracyjnym, który w zasadzie uruchamia testy przeciw publicznemu interfejsowi obiektu, na przykład:

public function testAddedRecipientCanBeFound()
{
    $emailAddress = '[email protected]';
    $forename = 'Fred';
    $surname = 'Smith';

    // First, delete the email address if it is already on the list
    $oldRecipient = $this->createRecipient();
    if($oldRecipient->find($emailAddress))
    {
        $oldRecipient->delete();
    }
    unset($oldRecipient);

    // Add the recipient using the test data
    $newRecipient = $this->createRecipient();
    $newRecipient->setForename($forename);
    $newRecipient->setSurname($surname);
    $newRecipient->setEmailAddress($emailAddress);
    $newRecipient->add();
    unset($newRecipient);

    // Assert that the recipient can be found using the same email address
    $this->assertTrue($this->_recipient->find($emailAddress));
}

Test „integracji” nie testuje żadnych wewnętrznych elementów klasy - po prostu upewnia się, że dany prawdziwy obiekt MCAPI zachowuje się jak w reklamie.

Czy to jest poprawne? Czy to najlepszy sposób na przeprowadzenie testu międzygranicznego? W końcu elementy wewnętrzne zostały przetestowane za pomocą testu jednostkowego. Czy mam rację, myśląc, że test integracji ma na celu sprawdzenie, czy naprawdę działa, zgodnie ze sposobem, w jaki reklamowane jest jego zachowanie?

Aby pójść o krok dalej, klasa MailChimpRecipient implementuje interfejs, który będzie również implementowany przez inne klasy. Chodzi o to, aby użyć fabryki, aby przekazać różne typy obiektów adresatów list mailowych do mojego kodu, które wszystkie robią to samo, chociaż używają różnych dostawców list mailowych. Ponieważ moje testy integracyjne testują ten interfejs, co powiesz na używanie go we wszystkich klasach, które implementują interfejs? Następnie, w przyszłości, jeśli zaprojektuję nową klasę, która będzie używana zamiennie, mogę uruchomić ten sam test integracji przed wstawieniem jej do projektu.

Czy to brzmi rozsądnie? Testy jednostkowe testują wnętrze obiektu, testy integracyjne upewniają się, że zachowuje się jak w reklamie?

Lewis Bassett
źródło
4
Myślę, że masz zbyt dużo logiki w teście. Uruchamiasz dużo kodu, dopóki nie potwierdzisz. Prawdopodobnie chcesz najpierw przetestować usunięcie odbiorcy. Ale to nie odpowiada na twoje pytanie, tylko komentarz.
hakre
1
Powinieneś skorzystać z tej setUpfunkcji, aby ustalić podstawy do uruchomienia testów. Jeśli dane wejściowe są niezdefiniowane, to nie można tak naprawdę przetestować. Dane wejściowe muszą być precyzyjne, ścisłe i zawsze takie same. Jeśli warunek wstępny testu nie jest spełniony, zamiast tego pomiń test. Następnie przeanalizuj, dlaczego przeskakuje i czy musisz dodać dodatkowe testy i / lub setUpnie jest to zrobione dobrze.
hakre
1
Nie należy też wpisywać twardych wartości testowych wewnątrz własnego testu, ale tworzyć członków tej klasy, aby mogli być dzieleni między testami (i zmieniani w centralnym miejscu) lub używać DataProvider(to funkcja oferująca dane wejściowe jako parametry testu).
hakre
1
Wprowadź w znaczeniu wszystkiego, na czym działa twoja funkcja testowa. Podczas testowania dodawania odbiorcy i chcąc upewnić się, że już nie istnieje, powinieneś przynajmniej potwierdzić usunięcie na wypadek, gdyby się ono pojawiło. W przeciwnym razie nie będzie zapewnione, aby test był wymagany.
hakre
1
+1 za dobre pytanie, ale także głosował za przeniesieniem do programistów. Wygląda na to, że należy do pytania o strategie testowania
GordonM

Odpowiedzi:

17

Podczas testowania kodu należy zwrócić uwagę na trzy obszary:

  • Testowanie scenariuszy
  • Testy funkcjonalności
  • Testów jednostkowych

Zwykle ilość testów, które przeprowadzasz w każdej kategorii, miałaby kształt piramidy, co oznacza dużo testów jednostkowych na dole, kilka testów funkcjonalnych na środku i tylko kilka testów scenariuszowych.

W teście jednostkowym wyśmiewasz wszystko, czego używa testowana klasa i testujesz to w czystej izolacji (dlatego ważne jest, aby upewnić się, że wewnątrz klasy odzyskujesz wszystkie zależności poprzez wstrzyknięcie, aby można je było zastąpić testem).

W testach jednostkowych testujesz wszystkie możliwości, a więc nie tylko „szczęśliwą ścieżkę”, ale także wszystkie błędy.

Jeśli masz całkowitą pewność, że wszystkie jednostki działają w izolacji, napisz kilka testów (testów funkcjonalnych), aby upewnić się, że działają one również w połączeniu. Następnie piszesz test scenariusza, który testuje okablowanie między wszystkimi modułami funkcjonalnymi.

Załóżmy na przykład, że testujesz samochód.

Możesz zmontować cały samochód i jako kierowca sprawdzić wszystkie możliwe warunki, ale byłoby to naprawdę trudne.

Zamiast tego przetestowałbyś małą część silnika ze wszystkimi możliwościami (test jednostkowy)

Następnie testujesz cały silnik (oddzielnie od samochodu), co byłoby testem funkcjonalnym.

W ostatnim teście wkładasz klucz, uruchamiasz samochód i jedziesz na parking. Jeśli to działa, wiesz, że wszystkie części (akumulator, paliwo, silnik, ...) są połączone, a ponieważ przetestowałeś je osobno, możesz być całkiem pewien, że cały samochód działa poprawnie.

Więc w twoim przypadku przetestowałeś wszystkie błędy i szczęśliwą ścieżkę w teście jednostkowym i wiesz, że musisz tylko przeprowadzić kompleksowy test z „prawdziwymi komponentami”, aby sprawdzić, czy okablowanie jest prawidłowe.

Kilka innych punktów,

  • Unikaj logiki warunkowej w teście jednostkowym. Jeśli musisz posprzątać, użycie pewnego rodzaju stanu globalnego i testy mogą nagle na siebie wpływać.
  • Nie podawaj żadnych danych, które nie są istotne dla testu. Gdybym zmienił imię lub nazwisko, czy test nie powiedzie się? Prawdopodobnie nie dlatego, że ważny jest adres e-mail, ale ponieważ wspominasz go wprost w teście, nie jestem pewien. Spróbuj spojrzeć na Wzorzec konstruktora, aby zbudować dane testowe i wyjaśnić, co jest naprawdę ważne.
Wouter de Kort
źródło
Dzięki, to potwierdza wiele z tego, co myślałem. Tylko dla wyjaśnienia - to NIE jest test jednostkowy. Napisałem już test jednostkowy, który testuje obiekt w całkowitej izolacji i ma 100% pokrycia kodu obiektu. Miał to być test integracyjny, aby upewnić się, że działa, gdy wstrzyknę do niego prawdziwy obiekt MCAPI. Muszę tylko usunąć wszystkich odbiorców dodanych do listy - to wszystko, co trzeba zrobić, i jest ono zaimplementowane, aby żaden z testów nie wpływał na siebie nawzajem. Co zasugerowałbyś zamiast tego?
1
Tak! Zrozumiałem, że już przeprowadziłeś testy jednostkowe. Czy obiekt MCAPI śledzi adresatów i czy należy to wyczyścić? Jeśli jest to „problem” strony trzeciej, nie można nic zrobić w teście integracji. Jeśli z drugiej strony śledzisz listę, powinieneś upewnić się, że unikasz globalnych danych (i singletonów), aby upewnić się, że testy nie wpływają na siebie nawzajem. W idealnym świecie sprzątanie rzeczy po rozpoczęciu / zakończeniu testu wskazuje na wadę projektową, ale w prawdziwym świecie nie zawsze można tego uniknąć.
Wouter de Kort
1
Dodałbym, że testowanie scenariuszy prawdopodobnie nie jest czymś, do czego PHPUnit jest odpowiedni. Yu może warto przyjrzeć się narzędziu, które można uruchomić w przeglądarce, np. Selenium, lub narzędziu, które może symulować przeglądarkę, takim jak jMeter.
GordonM
Dzięki chłopaki! Na pewno jest wiele do nauczenia się, jeśli chodzi o pisanie dobrego testowalnego kodu, prawda? Zamówiłem sobie kopię tej książki: amazon.co.uk/… . Mam nadzieję, że wszystko, co powiedzieliście, będzie miało więcej sensu po przeczytaniu tego. @ Wouter, właśnie usuwam adresata, ponieważ test spowodowałby dodanie adresu e-mail do listy. Usuwam go, aby lista nie miała wpływu na ten test.
1
@LewisBassett Nie jestem programistą Php, ale wzorce testowe xUnit ( amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054 ) są zdecydowanie dobrą lekturą. Również artykuły na misko.hevery.com/code-reviewers-guide są naprawdę interesujące.
Wouter de Kort