Testy integracyjne. Co i jak?

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
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:

Kopiuj
- 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:

Kopiuj
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?

Kopiuj
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.

mr_jaro
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Grudziądz/Bydgoszcz
  • Postów:5300
0

Nie do końca wiem w czym robisz, ale baza danych też musi być właściwa z tym że jest to inna baza, przeznaczona tylko na testy i po każdym teście wraca do stanu sprzed testu np trzymając wszystkie operacje w transakcji lub po każdym teście uruchamiając refresh na migracjach. Innymi słowy, jeśli projekt ma być używany z mysql to musisz robić testy na mysql a nie na sqllitle


It's All About the Game.
edytowany 2x, ostatnio: mr_jaro
Zobacz pozostałe 5 komentarzy
mr_jaro
@Juhas: zazwyczaj wypełniasz danymi specjalnymi, np robisz usera example@example.com a próbujesz się zalogować na test@example.com i patrzysz czy dostajesz 422. Ty musisz mieć takie dane w bazie w danym konkretnym teście aby sprawdzić to co chcesz sprawdzić.
JU
No tak, to rozumiem. Ale jeśli chcę dodać rekord do tabeli, no to tabela może być pusta, tak? Chyba że chcę testować unikalność kluczy, ale to bez sensu :)
mr_jaro
@Juhas: baza musi być taka jaka będzie po oddaniu do produkcji gdy jeszcze żaden user nic nie kliknął, nie pojawiła się żadna treść. Po czym wypełniasz baze danymi które mają być by zrobić konkretny test, test się wykonuje, po teście leci rollback. Już to pisałem wcześniej.
JU
No tak, po oddaniu do produkcji większość tabel będzie pustych. Rozumiem, że muszę wypełnić resztę.
mr_jaro
@Juhas: tylko te które potrzebujesz w konkretnym teście i tylko na czas konkretnego testu.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 8 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
13

Dawno temu miałem takie rozkminy, ale mi przeszło.
Przestałem jakoś specjalnie rozróżniać testy integracyjne.

Testy to testy:

  1. podaję warunki początkowe,
  2. zapuszczam działanie,
  3. sprawdzam wynik.

Czy po drodze (punkt 1) wypełniam jakąs hashmapę, czy 4 bazy danych to już szczegół implementacyjny.

Bywa, że ze względów praktycznych trzeba wydzielić inicjalizację bazy danych, żeby nie robić przed każdym testem (i wtedy rollbacki po teście).
Bywa, że trzeba dłuzej trwające testy jakoś oznaczyć, by ich np. nie odpalać za każdym razem u programisty.

Ale to są szczegóły.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Zobacz pozostałe 50 komentarzy
Gworys
Roy Oshorove. The Art Of Unit Test Edit 2. Przeczytaj rozdziały 1.1 - 1.6. Kent Beck wspominał też w ostatnim rozdziale o nadwrażliwości testów i testach długotrwałych.
Wibowit
6 rodziałów potrzeba, by zdefiniować unit test? Grubo.
Gworys
Zapewne jest to wersja dla tych apodyktycznych :D
Wibowit
Google zwraca "No results found for apodeptycznych". Książkę Kenta Becka przeczytałem w całości. Całkiem spoko.
Gworys
Przepraszam - apodyktyczny.
Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
1

Pisanie dobrych testów wynika przede wszystkim ze zrozumienia architektury.

W większości przypadków można uniknąć stub'a, czy mock'a wydzielając testowaną logikę do np. klasy statycznej.

To czy piszesz klasę z testami dla metody czy innej klasy to już zależy od twojej kreatywności. Ja lubie np. Partiale używać na inną ścieżke testów.

Ktoś tam coś mówił o słowniku. Co to znaczy mockować? :D

Na poważnie to kiedy ludzie zaczną odróżniać mock od stuba i fake.?

Nie ma nic złego w odróżnianiu testów. :P Tyle że pisanie samych testów integracyjnych, jest w większości przypadków słabe. Lepiej od razu pisać Funkcyjne/Akceptacyjne w stylu BDD z konkretnym label'em pod funkcjonalność.


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
edytowany 1x, ostatnio: Gworys
Zobacz pozostały 1 komentarz
jarekr000000
Co do mock, stub, fake - to fakt. Praktycznie nigdy nie używam mocków. Używam niestety potocznego, niepoprawnego nazewnictwa. Praktycznie to Tylko fake (są wyjątki, ale rzadkie) i to zarówno w testach jak i na produkcji. Produkcja to po prostu te fake implementacje, które spełniają wszystkie testy akceptacyjne :-)
Gworys
Ja wolę odróżniać stub od mock uważam, że testy wtedy zyskują na czytelności.
Gworys
Partial to jedna klasa w dwóch plikach.
jarekr000000
W ogólę nie lubię używać mocków, wyjątki to bardzo średnio zrobione zależności zewnętrzne, które tylko tak można przetestować. Raczej nie rozróżniam za to stub i fake. Stub to po prostu fake z trywialnymi implementacjami. Nie widze po co to rozróżnienie.
Gworys
No fake to tylko implementacja na potrzeby testów, która może być stub'em ewentualnie mock'iem :D
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2368
1
Gworys napisał(a):

...
Na poważnie to kiedy ludzie zaczną odróżniać mock od stuba i fake.?
..

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?

Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0
yarel napisał(a):
Gworys napisał(a):

...
Na poważnie to kiedy ludzie zaczną odróżniać mock od stuba i fake.?
..

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?

Jeśli ktoś mówi na rower samochód, to zwykle są ku temu tylko dwa powody, ale to może być subiektywne odczucie, więc bym się tym zbytnio nie przejmował. :P


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
edytowany 1x, ostatnio: Gworys
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 8 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2
Gworys napisał(a):

Jeśli ktoś mówi na rower samochód, to zwykle są ku temu tylko dwa powody, ale to może być subiektywne odczucie, więc bym się tym zbytnio nie przejmował. :P

powód pierwszy:
rower.jpg


jeden i pół terabajta powinno wystarczyć każdemu
Gworys
Poddaje się.
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2368
1
Gworys napisał(a):
yarel napisał(a):
Gworys napisał(a):

...
Na poważnie to kiedy ludzie zaczną odróżniać mock od stuba i fake.?
..

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?

Jeśli ktoś mówi na rower samochód, to zwykle są ku temu tylko dwa powody, ale to może być subiektywne odczucie, więc bym się tym zbytnio nie przejmował. :P

Cóż, różnica jest taka, że tu mamy zdefiniowany jasno kontekst (testy i implementacja na potrzeby testów), a w Twoich rozważaniach o rowerach i samochodach jakoś tego kontekstu nie wskazujesz. Obydwa są pojazdami, obydwa mogą być kupione itd.

W code review, takie rozróżnianie, u co bardziej zapalczywych jednostek, może prowadzić do patalogii typu: "nazwałeś plik FooStub, a to jest przecież FooMock" i tym samym odrywać ludzi od rozwiązywania właściwych problemów.

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

edytowany 1x, ostatnio: yarel
Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0
yarel napisał(a):
Gworys napisał(a):
yarel napisał(a):
Gworys napisał(a):

...
Na poważnie to kiedy ludzie zaczną odróżniać mock od stuba i fake.?
..

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?

Jeśli ktoś mówi na rower samochód, to zwykle są ku temu tylko dwa powody, ale to może być subiektywne odczucie, więc bym się tym zbytnio nie przejmował. :P

Cóż, różnica jest taka, że tu mamy zdefiniowany jasno kontekst (testy i implementacja na potrzeby testów), a w Twoich rozważaniach o rowerach i samochodach jakoś tego kontekstu nie wskazujesz. Obydwa są pojazdami, obydwa mogą być kupione itd.

W code review, takie rozróżnianie, u co bardziej zapalczywych jednostek, może prowadzić do patalogii typu: "nazwałeś plik FooStub, a to jest przecież FooMock" i tym samym odrywać ludzi od rozwiązywania właściwych problemów.

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

Ja bije do tego, że jeśli nie potrafisz, odróżnić jednego od drugiego to jest źle. To czy dopisujecie do nazwy stub, czy dummy, spy etc... To już problem spójnej konwencji, jaką przyjęliście.

Uważam również, że w komunikacji codziennej, kiedy mówimy o testach powinniśmy odróżniać stub od mock'a, ponieważ zmienia to kontekst wypowiedzi.

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

Tam gdzie czytelność testów ma znaczenie.


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
edytowany 3x, ostatnio: Gworys
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2368
1
Gworys napisał(a):

...

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

Ja bije do tego, że jeśli nie potrafisz, odróżnić jednego od drugiego to jest źle. To czy dopisujecie do nazwy stub, czy dummy, spy etc... To już problem spójnej konwencji, jaką przyjęliście.

Uważam również, że w komunikacji codziennej, kiedy mówimy o testach powinniśmy odróżniać stub od mock'a, ponieważ zmienia to kontekst wypowiedzi.

Ok, rozumiem Twój punkt widzenia.

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

Tam gdzie czytelność testów ma znaczenie.

Ten argument jest dość ogólny, bo czytelność kodu jest raczej powszechnie pożądaną cechą. Brakuje mi tu jednak jakiegoś powiązania między czytelność kodu i rozróżnianiem różnych implementacji. Masz jakieś przykłady, gdzie zmiana nazewnictwa jakoś istotnie wpłynęła na czytelność testów? np. "ServiceTestImpl zamieniliśmy na ServiceMockImpl i to uporządkowało nam testy". Nie wiem, dlatego dopytuję celem aktualizacji bazy wiedzy :-)

Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0
yarel napisał(a):
Gworys napisał(a):

...

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

Ja bije do tego, że jeśli nie potrafisz, odróżnić jednego od drugiego to jest źle. To czy dopisujecie do nazwy stub, czy dummy, spy etc... To już problem spójnej konwencji, jaką przyjęliście.

Uważam również, że w komunikacji codziennej, kiedy mówimy o testach powinniśmy odróżniać stub od mock'a, ponieważ zmienia to kontekst wypowiedzi.

Ok, rozumiem Twój punkt widzenia.

Gdzie, to rozróżnianie nazewnictwa implementacji w testach przynosi korzyść?

Tam gdzie czytelność testów ma znaczenie.

Ten argument jest dość ogólny, bo czytelność kodu jest raczej powszechnie pożądaną cechą. Brakuje mi tu jednak jakiegoś powiązania między czytelność kodu i rozróżnianiem różnych implementacji. Masz jakieś przykłady, gdzie zmiana nazewnictwa jakoś istotnie wpłynęła na czytelność testów? np. "ServiceTestImpl zamieniliśmy na ServiceMockImpl i to uporządkowało nam testy". Nie wiem, dlatego dopytuję celem aktualizacji bazy wiedzy :-)

Trochę mi to przypomina porównywanie repozytoriom do DAO.
A czym twoim zdaniem różni się Stub od Mock. Może rzeczywiście, jeśli nie widać różnicy to nietrzeba używać innej nazwy...?


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2368
1
Gworys napisał(a):

Trochę mi to przypomina porównywanie repozytoriom do DAO.
A czym twoim zdaniem różni się Stub od Mock. Może rzeczywiście, jeśli nie widać różnicy to nietrzeba używać innej nazwy...?

Nie widzę potrzeby granulacji nazw dla testowej implementacji czegoś. W myśl zasady: "Nie należy mnożyć bytów ponad potrzebę".
Nie wykluczam, że żyję w jaskini bez światła, a Ty przynosisz mi latarkę ;-)

Skorzystałem, z okazji, że wspomniałeś o tym, że ludzie tego nie rozróżniają, dlatego dopytuję o korzyści płynące z takiego podejścia ewentualnie skąd ta potrzeba granulacji.

Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0
yarel napisał(a):
Gworys napisał(a):

Trochę mi to przypomina porównywanie repozytoriom do DAO.
A czym twoim zdaniem różni się Stub od Mock. Może rzeczywiście, jeśli nie widać różnicy to nietrzeba używać innej nazwy...?

Nie widzę potrzeby granulacji nazw dla testowej implementacji czegoś. W myśl zasady: "Nie należy mnożyć bytów ponad potrzebę".
Nie wykluczam, że żyję w jaskini bez światła, a Ty przynosisz mi latarkę ;-)

Skorzystałem, z okazji, że wspomniałeś o tym, że ludzie tego nie rozróżniają, dlatego dopytuję o korzyści płynące z takiego podejścia ewentualnie skąd ta potrzeba granulacji.

Wynika z tego, iż są to dwa byty o innym zastosowaniu, które w niczym siebie nie przypominają (oprócz tego, że są tworzone na potrzeby testów).

Jeśli nie potrzebujesz takiego podziału to po prostu nie używaj postfiksu, który może wprowadzać kogoś w błąd lub powodować problem ze zrozumieniem twojej intencji twórczej.


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

Dobra, trochę odbiegamy od tematu. Czuję się jakbym rzucił kiełbasą w stado wilków :D
Kilka razy bywało, że uważałem, że @jarekr000000 opowiada głupoty, jednak w większości przypadków później przyznawałem mu rację. Albo chociaż częściową rację. Dlatego też chciałbym Cię prosić o jakąś kontynuację tematu. Tak samo @somekind, którego uważam za człowieka z dużą wiedzą (nie mówię, że inni jej nie mają, po prostu bacznie zawsze czytam posty tej dwójki).

Nie rozumiem za bardzo jak można nie odróżniać testów (tak zrozumiałem z wypowiedzi Jarka). Przecież główną zasadą testów jednostkowych jest testowanie elementów systemu w izolacji. Natomiast testy integracyjne mają zupełnie inny zamysł. Więc pytanie na jakiej płaszczyźnie nie rozróżniasz tych testów?

W ogóle chciałbym najlepiej zobaczyć jakieś Wasze praktyczne przykłady testów. O ile z testami jednostkowymi wydaje mi się, że nie mam problemów (zazwyczaj), to właśnie natrafiłem na pewien opór przy testach integracyjnych. I czytając wszystkie posty tak naprawdę nie wiem sam, czy robię w tym momencie testy integracyjne, funkcyjne, czy może akceptacyjne, bo teoretycznie wszystko by mi pasowało do tego, co robię teraz. Tu mogę zrozumieć nierozróżnianie tych testów, bo granica wydaje mi się dość cienka i płynna.

Wg mnie wystarczą 2 rodzaje testów, a 3 są ideałem:

  1. Testy jednostkowe
  2. Testy integracyjne (zwał jak zwał - testujące system jako całość)
  3. Testy przez człowieka

Podejrzewam też, że tak jest w większości przypadków. Dlatego pożądam Waszych kodów :D Serio, chciałbym zobaczyć jak piszecie testy, możecie coś pokazać? To nie musi być nic prawdziwego, to może być jakiś zmyślony system.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 8 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
0

Tu mam taki trochę złośliwy przykład:
https://github.com/javaFunAgain/ratpong/blob/master/src/test/java/pl/setblack/pongi/users/repo/UsersRepositoryTest.java

W środku masz jeden zestaw testów UserRepositoryBase
Odpalany raz z HashMapą w pamięci UsersRepositoryTest .
A raz z faktyczną bazą (nie SQL) UserPersistentRepoTest .

Pozwiedz teraz jaki to rodzaj testów...

Kopiuj
@Test
    public void shouldRegisterUserOnlyOnce() {
        this.usersRepository.addUser("irreg", "bbbb");
        final RegUserStatus result = this.usersRepository.addUser( "irreg" , "baaaa");
        assertThat ( result.ok, is( false));
    }

Sprawdzamy, że usera można rejestrować tylko raz (z danym loginem).

Przecież główną zasadą testów jednostkowych jest testowanie elementów systemu w izolacji.

Otóż uważam, że jest to niepraktyczne. Bo w izolacji od czego? Praktycznie zawsze zależysz od runtime, bibliotek, reszty modułów.
Przez nadużywanie tej koncepcji izolacji mam potem testy, które testują tylko mockito.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 14 godzin
2

Izolacja w testach jest ważna, ale w tym sensie, że testy powinny być od siebie nawzajem odizolowane, a więc nie powinny w żaden sposób na siebie wpływać. Stąd wynika iż jeśli np mamy globalny mutowalny stan, który jest zmieniany w testach to by testy były odizolowane muszą być wykonywane jednowątkowo, a globalny stan musi być za każdym razem przywracany do jednakowego stanu. Izolacja w takim scenariuszu może być bardzo trudna jeżeli jest sporo rzeczy, które dzieją się asynchronicznie. Projekt nad którym pracuję w robocie ma właśnie taki problem. Niektóre mikroserwisy są testowane tak, że jest globalny mutowalny stan, ograniczenie na jeden test jednocześnie na jedną JVMkę, resetowanie globalnego stanu między testami, a mimo tego izolacja nie działa i nie wiadomo dlaczego. Coś gdzieś jest pominięte przy resetowaniu stanu czy ubijaniu asynchronicznych zadań. Dążymy do tego by pozbyć się takich mikroserwisów i takiego projektowania kodu (gdzie izolacja jest trudna do osiągnięcia).

Często jednak izolację rozważa się w innym sensie. Nie izolację między testami, ale np izolację między różnie rozumianymi jednostkami.


"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
neves
  • Rejestracja:prawie 22 lata
  • Ostatnio:dzień
  • Lokalizacja:Kraków
  • Postów:1114
2

na przykładzie web api, 2 poziomy testów:

  1. test endpointa, szczęśliwe ścieżki + wszystkie inne do których ktoś by próbował ręcznie pisać żądania w Postmanie
  2. test serwisów aplikacyjnych, przypadki brzegowe

poziom 1)

Kopiuj
[TestClass]
public class Questions_HappyPath : BaseFixture
{
    private const string EndpointName = "Questions";
    private HttpClient client;


    [TestInitialize]
    public void TestInitialize()
    {
        factory = new ApiFactory(ApiFactory.DatabaseType.SQLiteInMemory);
        client = factory.CreateClient(Token);
    }

    [TestMethod]
    [DataRow(1, 1)]
    [DataRow(2, 2)]
    [DataRow(3, 4)]
    public async Task UpdateQuestionWithAnswers(long questionId, long answerId)
    {
        var command = new UpdateQuestion
        {
            Content = "Who is your momy?",
            Answers = new List<UpdateAnswer>
            {
                new UpdateAnswer { AnswerId = answerId, Content = "Alicia", IsCorrect = false }
            },
            CatalogId = 2,
            ConcurrencyToken = 0
        };

        var response = await client.PutAsync($"{EndpointName}/{questionId}/", command);

        response.EnsureSuccessStatusCode();
        var context = factory.GetContext<TestCreationDbContext>();
        var actualQuestion = context.Questions.Include(x => x.Answers).FirstOrDefault(x => x.QuestionId == questionId);

        AssertExt.AreEquivalent(command, actualQuestion);
    }
}

poziom 2)

Kopiuj
public class QuestionsServiceTests : BaseFixture
{
    private TestCreationDbContext TestCreationDbContext;
    private QuestionsService serviceUnderTest;

    private protected override DatabaseType GetDatabaseType()
    {
        return DatabaseType.SQLiteInMemory;
    }

    [TestInitialize]
    public void TestInitialize()
    {
        using (var context = CreateTestCreationDbContext())
        {
            TestUtils.Seed(context);
        }
        TestCreationDbContext = CreateTestCreationDbContext();
        var uow = TestUtils.CreateTestCreationUoW(TestCreationDbContext);
        serviceUnderTest = new QuestionsService(new QuestionReader(TestCreationDbContext), uow);
    }
    [TestCleanup]
    public void TestCleanup()
    {
        TestCreationDbContext.Dispose();
    }

    [TestMethod]
    [DataRow(ValidQuestionId, ResultStatus.Ok)]
    [DataRow(DeletedQuestionId, ResultStatus.NotFound)]
    [DataRow(NotExisitngQuestionId, ResultStatus.NotFound)]
    [DataRow(OtherUserQuestionId,   ResultStatus.Unauthorized)]     
    public void UpdateQuestionWithAnswers(long questionId, ResultStatus expectedResult)
    {
        var command = new UpdateQuestion()
        {
            Content = "Dani Carvajal",
            CatalogId = ValidQuestionsCatalogId,
            ConcurrencyToken = 0,
        };
        Result result = serviceUnderTest.UpdateQuestionWithAnswers(OwnerId, questionId, command);
        Assert.AreEqual(expectedResult, result.Status);
    }

    [TestMethod]      
    public void UpdateQuestionWithAnswers_GivenWrongConcurrencyToken_ShouldReturnResultConflict()
    {
        var command = new UpdateQuestion()
        {
            Content = "Dani Carvajal",
            CatalogId = ValidQuestionsCatalogId,
            ConcurrencyToken = 666,
        };
        Result result = serviceUnderTest.UpdateQuestionWithAnswers(OwnerId, ValidQuestionId, command);
        Assert.AreEqual(ResultStatus.Conflict, result.Status);
    }
}

Gworys
O co ci chodzi z tym pierwszym poziomem.?
neves
próba uniknięcia wdawania się w dyskusje integracyjne vs jednostkowe
Gworys
To się nie odzywam :P
AreQrm
  • Rejestracja:prawie 11 lat
  • Ostatnio:29 dni
  • Lokalizacja:Londyn
  • Postów:873
4

Ok to zacznijmy od początku...
Po co w ogóle testujesz? Chcesz wiedzieć, że coś działa prawidłowo. I jednocześnie nie chcesz tego sprawdzać ręcznie za każdym razem. Teraz jak to zrobisz... Logikę która jest ładnie wydzielona, można łatwo i szybko przetestować w izolacji w pamięci - tu testy jednostkowe błyszczą. I na tym skończę o jednostkowych bo napisałeś że tu jest super. Super.

Testy integracyjne z natury są wolniejsze. Często dotykają zewnętrznych zależności jak baza danych czy serwisy trzecich firm (zwykle testowe). To czy użyjesz mocka takiego serwisu, czy nie, będzie zależało od jego stabilności oraz Twoich potrzeb (uruchomienie testów bez dostępu do internetu). Czasem robi się dwa zestawy wtedy, jeden korzysta z np test api paypala, które częściej nie działa jak działa, i osobny zestaw je mockuje. Trudno, takie życie.
Z bazą danych podobnie. Zależy czego oczekujesz. Jeśli chcesz mieć 100% pewności że wszystko działa na np MySQL to nie użyjesz sqllite. Jeśli uważasz że sprawdzenie na sqlite wystarczy, a resztę się "przeklika" czy np testy e2e z UI sprawdzą - to zależy od Ciebie.
Testy integracyjne, też dają Ci świadomość żę nie tylko poszczególne kawałki działają, ale ładnie się ze sobą spinają. A także że działają rzeczy których nie ma sensu testować jednostkow (jakieś proste wrappery, DTO, konstruktor, pierdoły, serwisy które tylko wywołują inny serwis bez logiki (aka serwisy aplikacyjne)...
Z racji tych zależności i powolności warto je wydzielić do osobnego projektu bądź kilku projektów.
Teraz co testować, jakie przypadki to zależy jak dana część jest ważna. Np system płatności będziesz chciał przetestować pewnie też integracyjnie więcej przypadków, żeby nie było wtop. Jakiś mniej ważny workflow wystarczy happy path. I może jeden unhappy.

Generalnie, przy testowaniu i w ogóle przy tworzeniu oprogramowania zależy Ci na "fast feedback loop", czyli chcesz szybko wiedzieć, że zmiana jaką zrobiłeś działa lub nie. Więc dlatego chesz oddzielić testy wolne od szybkich. np, tutaj dołoże kilka różnych testów, Tobie wystarczy jeden zestaw integracyjnych pewnie:

Kompilacja (tak, to pierwsza "bariera" do przejścia i szybka informacja zwrotna czy coś zadziała.
Unit testy
Testy integracyjne (in memory db)
Testy integracyjne z third party test serwisami - zamockowane i stabilne.
Testy integracyjne na oracle - odpalają się tylko na CI, lokalnie nie używasz w ogóle.
Testy integracyjne z third party test serwisami - niestabilne, dlatego zostały wydzielone.


WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 7 godzin
  • Postów:5109
0

Jeżeli chodzi o nazewnictwo, to po co w ogóle rozróżniać i bić się o terminologię? test = test

edytowany 1x, ostatnio: WeiXiao
somekind
A kiwi kiwi kiwi.
Silv
@somekind: przez Ciebie aż musiałem wejść na inne forum niż 4p, by dowiedzieć się, o co chodzi. ;)
AreQrm
Zombii zombii zombii
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 8 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Trochę z boku topic, ale w zasadzie w temacie.
ZIO - cudowne dziecko Johna De Goes - jaram się jak junior 15k.( Ale jeszcze nigdzie ZIO nie użyłem :-) )

https://scalaz.github.io/scalaz-zio/overview/testing_effects.html

Niby nic tam nowego nie ma, testowanie pure FP tak w zasadzie wyglądało zawsze. Ale teraz może uda sie wprowadzić standard na efekty. Monada IO w Scali, ogólniejsza i chyba lepsza w praktyce od IO z haskella. Zobaczymy.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 4x, ostatnio: jarekr000000
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 14 godzin
0

Ale teraz może uda sie wprowadzić standard na efekty.. monada IO w Scali, ogólniejsza i chyba lepsza w praktyce od IO z haskella. Zobaczymy.

W Scali (ale też np w Javie, Kotlinie, C++, etc) jest podstawowy problem z efektami ubocznymi - kompilator w żaden sposób przed nimi nie przestrzega (nie mówiąc już o blokowaniu ich). W Haskellu nie mogę sobie przypadkowo zrobić x = x + 1 ani żadnego innego efektu ubocznego poza monadą IO. W Scali natomiast mogę przypadkowo odpalić np stringBuilder.append(5) bez opakowywania tego w monadę IO i nie będzie żadnego ostrzeżenia. Używanie monad IO w Scali wymaga dyscypliny, by nie wywołać efektu ubocznego poza przeznaczonymi do tego konstrukcjami.


"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.
jarekr000000
Żeby było śmieszniej... chyba wczoraj pisałem to właśnie do @Krolik a - czemu scali daleko do Haskella.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 23 godziny
  • Lokalizacja:Wrocław
3
Juhas napisał(a):

Dobra, trochę odbiegamy od tematu. Czuję się jakbym rzucił kiełbasą w stado wilków :D

Raczej dżdżownic.

W ogóle chciałbym najlepiej zobaczyć jakieś Wasze praktyczne przykłady testów. O ile z testami jednostkowymi wydaje mi się, że nie mam problemów (zazwyczaj), to właśnie natrafiłem na pewien opór przy testach integracyjnych. I czytając wszystkie posty tak naprawdę nie wiem sam, czy robię w tym momencie testy integracyjne, funkcyjne, czy może akceptacyjne, bo teoretycznie wszystko by mi pasowało do tego, co robię teraz. Tu mogę zrozumieć nierozróżnianie tych testów, bo granica wydaje mi się dość cienka i płynna.

Nie czytałem żadnych książek o tej tematyce, więc nie znam oficjalnych definicji, posługuję się intuicyjnymi:

  • Testy akceptacyjne robi klient/BA/Product Owner albo ktoś inny odpowiadający za wymagania. To nie jest sprawdzanie, czy aplikacja działa i czy 2+2=4, tylko czy dana funkcja robi to, czego klient w ogóle chciał.
  • Testy funkcjonalne to testy uruchamiane na działającej aplikacji i sprawdzające czy działa i spełnia wymagania. Nie wymagają wiedzy o wewnętrznej implementacji, więc często są tworzone przez testerów.
  • Testy integracyjne są najtrudniejsze, bo różni ludzie nazywają tak różne rzeczy.
    a) Dla jednych to testy sprawdzające, czy klasa X działa ze swoimi zależnościami. (Bo w swoich "testach jednostkowych" zawsze mockują wszystkie zależności testowanej klasy.)
    b) Dla innych to testy sprawdzające działanie jakichś komponentów systemu z innymi komponentami systemu na żywo. Np. czy klasa przetwarzająca dane dane z jakiegoś API/bazy/pliku poprawnie przetwarza dane faktycznie pobrane z tego API/bazy/pliku, a nie z jakiegoś mocka w pamięci.
    c) Dla jeszcze innych to to samo, co testy funkcjonalne - bo de facto jedne i drugie sprawdzają zachowanie tworzonej aplikacji/serwisu/API z jego zależnościami w postaci

Moja osobista opinia jest taka, że:
a) to patologia i kompletne niezrozumienie idei testowania jednostkowego;
b) czasami ma sens (np. napisaliśmy swojego ORMa, i chcemy sprawdzić, czy dane odczytane z bazy są takie same jak te wstawione), ale często też jest oznaką, że jakiś projekt jest bardzo "enterprise" i cierpi na nadmiar warstw, komponentów i porąbanych technologii (mam takie rzeczy w pracy, np. testy integracyjne sprawdzające czy da się utworzyć obiekt bazując na konfiguracji IoC w XML albo czy jakieś klasy ze środkowej warstwy fizycznie kontaktują się z API, z którego korzystają). Nie mówię, że istnienie takich testów jest złe, po prostu ich pisanie było stratą czasu wynikającą z błędów architektury lub doboru technologii.
c) mam takie wrażenie, że w typowym nieprzekombinowanym projekcie, testy integracyjne i funkcjonalne się zrównują, bo testujemy, czy uruchomiona aplikacja działa prawidłowo i daje oczekiwane rezultaty współpracując z faktyczną bazą/plikami/zewnętrznymi usługami.

Juhas napisał(a):

Jeden plik testuje jedną klasę

Załóżmy, że to jakaś klasa utilsowa, która ma 10 metod. Załóżmy, że dla każdej metody rozpatrujemy 10 przypadków dla prawidłowych danych (typowe i brzegowe warunki) oraz 10 przypadków, kiedy metoda zwraca błąd/rzuca wyjątek. Załóżmy, że piszemy najkrótsze możliwe przypadki testowe, czyli: 3 linijki na kod testowy (arrange, act, assert), 2 linijki odstępu między poszczególnymi fazami, 3 linijki na sygnaturę metody oraz klamerki i linijkę na odstęp między metodami testowymi. Daje to razem 9 linijek na test.
10 * 20 * 9 = 1800 linijek kodu w pliku. Dla mnie to nie brzmi jak coś prostego.

Czasami może i zdarza mi się, że mam wszystkie testy jednej klasy w jednym pliku. Ale to chyba tylko wtedy, gdy klasą jest jakiś handler/serwis z jedną metodą publiczną i prawie nie zawiera logiki. Znacznie częściej do jednej testowanej metody mam wiele plików: oddzielne na błędy walidacji wejście, oddzielne na wyjątki rzucane podczas przetwarzania, oddzielne pozytywny przepływ - a i to nieraz dzielę, jeśli mam jakiś setup wspólny dla kilku przypadków testowych, gdy inne przypadki wymagają innego setupu.

Jeśli chodzi o testy integracyjne, to czytam różne rzeczy. Że tu testujemy np. tylko happy path. Czy to jest reguła?

Nie wiem, czy to reguła, ale dla mnie testy wyłącznie happy path to de facto brak testów. Bo nie da się testami udowodnić, że kod zadziała w każdym przypadku, ale musisz pokazać, w jakich przypadkach zgodnie z oczekiwaniami nie zadziała.

Ja do wszystkiego używam nUnit.

Ja również. Ale w przypadku testów integracyjnych "po mojemu" pisanych do API webowego nie ma problemu, aby użyć w ogóle innej technologii. W końcu request HTTP można podobno wysłać nawet w JS.

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.

Z pragmatycznego punktu widzenia prościej stworzyć SUT z automatycznymi mockami wszystkich ulotnych zależności oraz automatycznym stubowaniem zwracanych danych. Jak trzeba, to się po prostu stubuje po swojemu, to co się chce i można weryfikować wywołania metod. 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.

kzkzg
Po co pisac osobna metode dla kazdego przypadku testowego?
somekind
W ogólności po nic, struktura kodu testującego zależy od kodu testowanego oraz celu testów. Czasem da się kod przypadków testowych uwspólnić, a czasem się nie da.
Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0

Z pragmatycznego punktu widzenia prościej stworzyć SUT z automatycznymi mockami wszystkich ulotnych zależności oraz automatycznym stubowaniem zwracanych danych.

A dlaczego nie CUT, OUT, Substitute.? Czy ten SUT to nie jest też jakieś zboczenie.?

Jeśli do stubowania jednego parametru potrzebujesz frameworka izolacji to prawdopodobnie sam się nabawiłeś jakiegoś dziwnego fetyszy od kolegów z pracy. :D

Testy akceptacyjne robi klient/BA/Product Owner albo ktoś inny odpowiadający za wymagania. To nie jest sprawdzanie, czy aplikacja działa i czy 2+2=4, tylko czy dana funkcja robi to, czego klient w ogóle chciał.

A jak sprawdzić, czy dana funkcja robi to, co chce klient, jeśli aplikacja nie działa, czyli nie dodaje 2+2? Kto ci takich bzdur nagadał.?
Testy Akceptacyjne mają gwarantować akceptacje oprogramowania przez klienta. Może to tylko u was klienci akceptują aplikacje, które nie działają.
Testy akceptacyjne nie robi klient.
Testy akceptacyjne wykonuje się z klientem/Product Owner'em.


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 23 godziny
  • Lokalizacja:Wrocław
0
Gworys napisał(a):

A dlaczego nie CUT, OUT, Substitute.? Czy ten SUT to nie jest też jakieś zboczenie.?

Użyłem nazwy, która jest zrozumiała dla wszystkich.
No, najwyraźniej prawie dla wszystkich.

Jeśli do stubowania jednego parametru potrzebujesz frameworka izolacji to prawdopodobnie sam się nabawiłeś jakiegoś dziwnego fetyszy od kolegów z pracy. :D

Nie potrzebuję stubować jednego parametru, nie mam takich malutkich projektów.

A jak sprawdzić, czy dana funkcja robi to, co chce klient, jeśli aplikacja nie działa, czyli nie dodaje 2+2? Kto ci takich bzdur nagadał.?

Napisałem, co jest celem testów akceptacyjnych - sprawdzanie zgodności z wymaganiami, a nie samo działanie, bo to jest weryfikowane innymi testami znacznie wcześniej.
To, że nie umiesz czytać ze zrozumieniem to już nie mój problem.

Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0

Użyłem nazwy, która jest zrozumiała dla wszystkich.
No, najwyraźniej prawie dla wszystkich.

Jak to dobrze wypowiadać się za wszystkich. :D

Nie potrzebuję stubować jednego parametru, nie mam takich malutkich projektów.

A co mnie obchodzą twoje projekty.? Masz jakąś manie wielkości.? :D Rozumiem, że nie uświadczysz w owych projektach obiektów z metodami, które mają 1 parametr, super :D

Napisałem, co jest celem testów akceptacyjnych - sprawdzanie zgodności z wymaganiami, a nie samo działanie, bo to jest weryfikowane innymi testami znacznie wcześniej.
To, że nie umiesz czytać ze zrozumieniem to już nie mój problem.

To dlaczego te testy nie nazywają się testy zgodności wymagań.? HAHA :D


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
edytowany 1x, ostatnio: Gworys
Zobacz pozostały 1 komentarz
mr_jaro
@Silv: ja tam lubię wypowiedzi somekinda, kibicuję mu tu, zobaczymy co z tego wyjdzie :)
Silv
@mr_jaro: ale ja bym chciał poczytać o testach, a od tej ironii i docinków odechciewa mi się. :(
Gworys
Aha potem przyłażą do mnie i mi mówią, że DDD nie może istnieć bez repozytorium, bo Somekind tak napisał na forum albo inne infantylne bzdury.
Silv
@Gworys: nie "bzdury", tylko inny punkt widzenia.
Gworys
Oczywiście. :D
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 23 godziny
  • Lokalizacja:Wrocław
0
Gworys napisał(a):

A co mnie obchodzą twoje projekty.?

Skoro cię nie obchodzą, to po co pytasz? Jak dla mnie EOT.

@Silv - jeśli masz jakieś konkretne pytania, to po prostu pytaj.

Silv
Akurat nie mam konkretnych pytań; przysłuchuję się dyskusji, która może mnie czegoś nauczyć.
Silv
Nie jest to to samo, co długi post na jakimś blogu, ale jakoś mi przyjemniej ciekawe dyskusje czytać niż blogi (które też czytam – w swoim czasie).
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
4

Bez sensu wymyślać kilkanaście nic nieznaczących nazw, łatwiej podzielić po czasie i wyśiłku wymaganego do zrobienia testu:
-takie które odpala się z IDE i trwają kilka sekund (odpalane przy każdej zmianie)
-takie które trwają dłużej, ale fajnie je mieć bo działają na żywym organizmie (zazwyczaj odpalane raz przed merge)
...i tyle.
Resztę testów mnie jako deva nie obchodzi bo to nie moja odpowiedzialność.

A co do tego czy coś jest fake, stubem czy innym wynalazkiem. Nazwij to po prostu InMemoryCostam i problem z głowy


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 7 godzin
  • Postów:5109
4

@danek

a o czym później ludzie będą opowiadać na konferencjach i pisać na blogach?

edytowany 2x, ostatnio: WeiXiao
danek
w sumie najs, najpierw X lat opowiadać o nowych terminach, a potem Y lat uczyć jak wszystko uprościć 
WeiXiao
i nazywać to innowacyjne/współczesne
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 14 godzin
4

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. Może być cholernie wolny, ale jeśli zysk z niego jest wystarczająco duży, to co z tego? Mogę sobie pogrupować testy (otagować) i jedną komendą odpalać tylko szybkie testy, a drugą komendą odpalać wszystkie testy. Jak kto woli.

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 wolę skupić się na praktycznych aspektach testów, a więc kategoryzuję n.p. tak:

  • samodzielne testy, które nie wymagają żadnych działających środowisk (tzn uprzedniego stawiania i konfigurowania aplikacji na jakichś maszynach), ale wystarczy jest odpalić prostym skryptem i działają na każdym kompie z poprawnie skonfigurowanym build toolem
  • testy wymagające zewnętrznych środowisk - takie testy mogą być poprawnościowe lub wydajnościowe

To jaką ktoś przyczepi etykietkę do testu to sprawa drugorzędna lub trzeciorzędna. I tak każdy ma inne intuicje i okazuje się, że termin np testy akceptacyjne jest przez każdego inaczej rozumiany. Zamiast tego lepiej dogadać się jakie katergorie testów są ważne w konkretnym projekcie. Dla przykładu w firmie mamy taką sytuację:

  • mamy wiele mikroserwisów i każdy ma builda w TeamCity
  • używamy więc często terminologii "testy serwisu A", "testy serwisu B", etc
  • zwykle jakiego rodzaju są testy w środku danego serwisu nie ma specjalnie znaczenia o ile te testy nie łączą się poza maszynę która je odpala ani nie wymagają specjalnego przygotowania maszyny (np stawianie apek)
  • jednak jest pewien wyjątek - mamy specjalny framework do tworzenia testów (nazywa się BackGarden) i operuje on na globalnym mutowalnym stanie, klika poprzez Selenium, stawia kilka JVMek, komunikuje się Akką między JVMkami i robi jeszcze inne dziwne rzeczy - te testy często padają i trzeba puszczać builda w kółko, więc wystarczy powiedzieć, że padają nam testy BackGardenowe i wszyscy wiedzą, że zadanie jest prawie gotowe, ale trzeba upolować zielonego builda
  • sporo testów jest w buildach, które nie dotyczą żadnego mikroserwisu, tzn build zawiera tylko kod testowy, gdzie testy są poprawnościowe czy wydajnościowe
  • pewne testy (np testy end-to-end) łączą się do np środowiska UAT, więc jeśli padają to zwykle oznacza iż z tym środowiskiem jest coś nie teges (zła konfiguracja mikroserwisu, wypięcie się mikroserwisów z Kafki, itd). Jeśli natomiast działają to dają większą pewność, że system działa na świeżych realnych danych niż testy ze sztywnymi danymi (tzn te w kodzie mikroserwisów), które ktoś kiedyś wrzucił do repozytorium VCSs.
  • część testów wymaga łączenia się z zewnętrznymi serwisami (np spoza naszej firmy w ogóle), które w pewnych porach nie działają, np w środę przez parę godzin pewne testy nam nie mają prawa iść, więc je w tym czasie ignorujemy

Powyższa kategoryzacja jest konkretna i ma znaczenie zarówno dla programistów jak i menedżera, biznesu, a także zewnętrznych zespołów. 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.


"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.
Gworys
  • Rejestracja:około 6 lat
  • Ostatnio:prawie 6 lat
  • Postów:139
0
danek napisał(a):

Bez sensu wymyślać kilkanaście nic nieznaczących nazw, łatwiej podzielić po czasie i wyśiłku wymaganego do zrobienia testu:
-takie które odpala się z IDE i trwają kilka sekund (odpalane przy każdej zmianie)
-takie które trwają dłużej, ale fajnie je mieć bo działają na żywym organizmie (zazwyczaj odpalane raz przed merge)
...i tyle.
Resztę testów mnie jako deva nie obchodzi bo to nie moja odpowiedzialność.

A co do tego czy coś jest fake, stubem czy innym wynalazkiem. Nazwij to po prostu InMemoryCostam i problem z głowy

Moim zdaniem wy po prostu nie rozumiecie różnicy pomiędzy jednym a drugim. Więc najłatwiej jest to po prostu wyśmiać lub stanąć po stronie jakiegoś urojonego lidera.


Unhandled Exception: System.MissingMethodException: Constructor on type 'System.Exception' not found.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)