Testy integracyjne. Co i jak?

Odpowiedz Nowy wątek
2019-05-13 11:00
0

Hej, to moje pierwsze podejście do testów integracyjnych i chciałbym się dowiedzieć jak to prawidłowo robić i jakie mam błędy w rozumieniu tematu. Sporo na ten temat przeczytałem, ale nie wszystko jest dla mnie jasne.

Z testami jednostkowymi mam prosto:
na każdy projekt (który podlega testom jednostkowym) jest jeden projekt testowy. Jeden plik testuje jedną klasę, np:

- solution
    - source
        + ModelsProject
        + ClientProject
    - tests
      - unitTests
        - ModelsTestProject
            Model1Tests.cs
            Model2Tests.cs
            Model3Tests.cs
        - ClientTestProject
          ...

Do takiej hierarchii można to uprościć. To mi się sprawdza, jest przejrzyste i działa. I super. Jednak teraz chciałbym dołożyć testy integracyjne i nie za bardzo wiem, jak się do tego zabrać. Rozumiem, że tutaj działamy na żywym organizmie. Mockowanie ograniczam właściwie jedynie do interakcji z użytkownikiem (np. okna dialogowe). Baza danych to SQLite w pamięci. Tylko nie za bardzo teraz wiem, jak to zrobić dobrze. Miałem już kilka podejść, jednak za każdym razem mam nieodparte wrażenie, że coś robię źle.

Ja wiem, że to zależy głównie od systemu, ale jak tutaj powinna wyglądać taka hierarchia projektów i klas? No bo projekt testowy na jeden projekt w solucji (tak jak mam przy testach jednostkowych) w tym momencie jest bez sensu. Bo projekty współdziałają ze sobą. Więc Wpadłem na pomysł, żeby dać różne projekty na testowanie różnych klientów. Np: WpfIntegrationTests, XamarinIntegrationTests, WebIntegrationTests... Wydaje się to logiczne. Więc idźmy dalej. Co tak naprawdę powinienem testować? W tym momencie zacząłem od testów WpfClient i MainViewModelu. Jednak szybko okazało się, że w MainViewModel nie mam tak naprawdę za wiele do testowania, bo większość akcji jest na poziomie innych viewmodeli. Ale to chyba nie jest problem.

Kolejnym problemem jest co i jak testować. Przykładowo podczas unit testów tworzę sobie metody w standardowym stylu:

public void AddItem_ItemIsNull_ThrowArgumentNullException()
{
//
}

public void AddItem_ItemIsNotNull_AddsItem()
{
//
}

public void AddItem_ItemIsNotNull_AddsItem()
{
//
}

itd. czyli testuję brzegowe wartości, jakieś wartości, które są ok i jakieś wartości, które są nie ok (np. item = null).

Jeśli chodzi o testy integracyjne, to czytam różne rzeczy. Że tu testujemy np. tylko happy path. Czy to jest reguła? Czy też powinienem testować podanie np. niepoprawnych wartości? No i jak nazywać takie metody?

public void CreateDocumentTest()
{
//
}

public void SaveDocumentTest()
{
//
}

public void LoadDocumentTest()
{
//
}

Czy jakoś inaczej? Jak to robicie? Jakie macie doświadczenia i jakie systemy sobie wypracowaliście? I czy używacie do tego innej biblioteki niż do jednostkowych? Ja do wszystkiego używam nUnit.

Pozostało 580 znaków

2019-05-18 02:17
0
yarel napisał(a):

Tak, z ciekawości, gdzie jest wartość dodana w nazywaniu na różne sposoby (mock, stub, fake) implementacji użytej na potrzeby testów ?
To nie jest tak jak z tym dowcipem o juhasach i bacy, który rozwiewa wątpliwości jak nazywać jeża poprawnie?

Myślę, że chodzi o wyrywanie lachonów z dziwnymi fetyszami. ;)

A tak na serio, to nie sądzę aby ktoś Ci odpowiedział na to pytanie. Bo odpowiedzieć mogą tylko ludzie, którzy faktycznie piszą tego typu klasy, czyli jaskiniowcy, a oni jeszcze nie wynaleźli języka.

@somekind, żeby nie zostać jaskiniowcem specjalnie rejestruje wszystkie klasy w kontenerze, żeby mógł je potem automatycznie stubować, mockować. :D

I nie trzeba się męczyć z dyskusjami na temat tego, czy prawidłowo się nazwało fejkowe klasy w projektach testowych. No chyba, że ktoś chce mieć wąską specjalizację, zostać senior test double developerem i debatować w nieskończoność, czy mock jest spy'em czy odwrotnie.

No tobie na pewno zajmie co najmniej tydzień, żeby, ustalić co jest mockiem a co stubem.


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
a po co komu w testach kontener ;) - danek 2019-05-18 02:28

Pozostało 580 znaków

2019-05-18 03:10
1
Wibowit napisał(a):

Teza o szybkości testów jest słaba. Nieraz jak testuję np jakiegoś aktora Akkowego to test może trwać nawet kilka sekund, bo takie sobie ustawiam timeouty. Operacje na aktorach dzieją się asynchronicznie, a test działa synchronicznie, więc trzeba robić awaity z timeoutami. Z drugiej strony mogę mieć test, który tworzy pliki na dysku, ale działa 10x szybciej. Co z tego wynika? To, że test jednostkowy niekoniecznie musi być szybki.

Raczej to, że porównujesz jabłka z poziomkami, co jakby nie bardzo ma sens. Nie chodzi o to, że jedne testy jednostkowe mogą być szybsze, a drugie wolniejsze, bo to jest truizm. Test funkcji X, do którego dane są dostarczone w pamięci będzie szybszy niż test funkcji X, do którego dane trzeba wczytać z jakiegoś pliku.

Zamiast się głowić jak kategoryzować testy albo powstrzymywać się przed stworzeniem tymczasowego pliku w teście jednostkowym w obawie, że ktoś się wścieknie

Pytanie zasadnicze - po co tworzyć pliki w testach jednostkowych? Jednostkowo testuje się przetwarzanie danych, jest wejście do którego przekazujemy wejściowe dane testowe, i wyjście, którego wynik porównujemy ze wzorcem. To wszystko. Trzeba mieć jakąś dziwną architekturę, żeby gdzieś tam po drodze się tworzyły pliki.

Natomiast to czy ja sobie w jakimś teście, który do tej pory był 100% unitowy (według wszelkich wyrytych w skałach zasad) zacznę tworzyć tymczasowy plik na dysku to jest taka pierdoła, że nie warto się nad tym pochylać. Jeśli stworzenie tymczasowego pliku umożliwi stworzenie sensownego testu (o wciąż akceptowalnej wydajności), gdzie lepiej pokrywam rzeczywisty kod biznesowy, a nie robię kolejnego testu Mockito to jak najbardziej warto taki tymczasowy plik stworzyć i zapomnieć o mockach przy testach danej klasy.

Teraz już jestem na 100% pewny, że to o jakąś dziką architekturę musi chodzić, skoro plikami zastępujesz mocki.

Gworys napisał(a):

@somekind, żeby nie zostać jaskiniowcem specjalnie rejestruje wszystkie klasy w kontenerze, żeby mógł je potem automatycznie stubować, mockować. :D

Niczego w żadnym kontenerze nie rejestruję, nawet żadnego nie mam.

No tobie na pewno zajmie co najmniej tydzień, żeby, ustalić co jest mockiem a co stubem.

Zajmuje sekundę, tylko po prostu nie mam takiej potrzeby, bo nie piszę ich ręcznie.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
Pokaż pozostałe 8 komentarzy
I gdzie tu mowa o tym: specjalnie rejestruje wszystkie klasy w kontenerze, żeby mógł je potem automatycznie stubować, mockować? Gdzie ja napisałem, że framework jest niezbędny do tworzenia mocków? - somekind 2019-05-20 03:06
No chyba nie zabronisz mi pisać z taką samą manierą jak ty...? - Gworys 2019-05-20 03:12
Czyli jesteś tym drugim. No cóż, nie mogę nic poradzić, to przecież nie twoja wina. - somekind 2019-05-20 03:13
@Gworys: @somekind pomaga, radzi nam. W ogóle czyta to, co tam się wypisuje. Nie jest tego wiele, no ale że mu się chce... - Silv 2019-05-20 03:51
A no widziałem. - Gworys 2019-05-20 04:07

Pozostało 580 znaków

2019-05-18 12:59
2

Test funkcji X, do którego dane są dostarczone w pamięci będzie szybszy niż test funkcji X, do którego dane trzeba wczytać z jakiegoś pliku.

Jeśli moje dane testowe mają kilka kilobajtów to wczytywanie ich z pliku będzie szybsze niż kompilacja klasy z takimi danymi osadzonymi w środku. Poza tym mając osobny plik łatwiej takie dane przejrzeć (np otworzyć w Excelu, przegrepować, etc) czy zastąpić innymi.

Nawet pomijając czas kompilacji czy wygodę programisty, to mając napęd SSD wczytywanie pliku będzie prawie na pewno wielokrotnie szybsze niż przetwarzanie go w testach - nie dość, że ładowanie klas implementujących testy (bądź do nich potrzebnych) sporo trwa to jeszcze dochodzi rozgrzewanie JITa, które trwa raczej wielokrotnie dłużej niż wczytywanie nawet wielokilobajtowych plików.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit, 2019-05-18 15:08

Pozostało 580 znaków

2019-05-20 02:39
0
Wibowit napisał(a):

Jeśli moje dane testowe mają kilka kilobajtów to wczytywanie ich z pliku będzie szybsze niż kompilacja klasy z takimi danymi osadzonymi w środku.

Benchmarkowałeś to? Jakoś nie wierzę, że operacja I/O i deserializacja będą szybsze niż skompilowanie. No, a poza tym, to kompilacja jest jednorazowa, a czytanie z dysku musi się odbywać za każdym razem, czyż nie?

Nawet pomijając czas kompilacji czy wygodę programisty

No właśnie - wygoda programisty. Wolę mieć dane statycznie typowane, z błędami w danych wejściowych wyłapywanymi przez kompilator, niż przeszukiwać pliki pod kątem błędów typów po jakichś zmianach w strukturze danych.
Nawet, gdyby kompilacja trwała 100 razy dłużej, to i tak w ostatecznym rezultacie będzie to szybsze podejście.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0