Jeśli miałbym napisać testy do swojej gierki to jak to mądrze zrobić?

0

Zastanawiałem się czy dać to jako osobny temat czy połączyć z tym poprzednim, w końcu zdecydowałem się dać jako osobny, bo to chyba są jednak osobne kwestie. Jeśli źle zrobiłem, to przepraszam.

Tym razem w kwestii tej mojej gierki, którą robię na razie sam dla siebie, a której robienie wyrywkowo opisywałem na mikroblogu. Robiąc ją przyjąłem odgórne założenie, że dopóki nie będę miał działającego prototypu z minimalną zaplanowaną funkcjonalnością, to skupię się WYŁĄCZNIE na zrobieniu tego prototypu, nie dbając zbytnio o takie kwestie jak "jakość kodu" itp; bo samo zrobienie prototypu jest już dość trudne i jeżeli będę dodatkowo chciał to zrobić dobrze, to się pewnie okaże, że ani nie będę miał tego zrobionego dobrze, ani wcale. Dlatego sprzątanie kodu jest zaplanowane dopiero na czas kiedy ten prototyp będzie. I dlatego też nie pisałem jak dotąd do tego żadnych testów, jako iż testowania niestety nigdy się nie nauczyłem.

Jednak pisząc ten prototyp staram się na boku myśleć (i zbierać rady m.in. z tego forum) jak to naprawdę powinno wyglądać. I jak, po zrobieniu wreszcie tego prototypu, powinienem zabierać się za sprzątanie. M.in. zatem zastanawiam się nad testami.

W tym celu przeczytałem m.in. następujące artykuły:

Pierwsze dwa uświadomiły mi, że - w przeciwieństwie do tego, co do tej pory uważałem - dobre testowanie NIE polega na pisaniu testu o nazwie testx do każdej funkcjoprocedurometody o nazwie x i testu/ów o nazwie testX do każdej klasy o nazwie X. Nie powinno być odpowiedniości 1-1 między testami a kodem. Szczerze mówiąc, cieszy mnie to: choć (jak pisałem) nigdy nie próbowałem porządnie testować, jednak zawsze wyobrażałem sobie, że testowanie w ten sposób będzie niemiłosierne, bo każda najdrobniejsza zmiana w kodzie będzie wymagać chrzanienia się z testami. Dobrze więc: teraz już bez wyrzutów sumienia nie mam zamiaru pisać testu do każdej metody.

Trzeci artykuł pokazał, że raczej należy testować pojedyncze części składowe systemu w całości:

Uncle Bob breaks this kata down into the following five tests:

  1. Gutter game scores zero - When you roll all misses, you get a total score of zero.
  2. All ones scores 20 - When you knock down one pin with each ball, your total score is 20.
  3. A spare in the first frame, followed by three pins, followed by all misses scores 16.
  4. A strike in the first frame, followed by three and then four pins, followed by all misses, scores 24.
  5. A perfect game (12 strikes) scores 300.

Ah! Więc nawet można po prostu testować input / outptut bez zagłębiania się w szczegóły implementacyjne! Fajnie!

Myślałem sobie, jak by to zastosować do mojej gry? Mimo bałaganu pewien podział jest: Mamy mianowicie BattleState która przyjmuje polecenia (ruchy) od graczy i zwraca listę BattleUpdates. Wyobrażałem sobie zatem, że BYĆ MOŻE testy powinny wyglądać tak: (1) budujemy (sztucznie) BattleState (2) być może wydajemy polecenie że gracz A robi ruch B; (3) Puszczamy jedną turę; (4) Sprawdzamy, czy zwrócone BattleUpdates są OK. Dzięki temu zmiana szczegółu implementacyjnego jakiejkolwiek pierdółki w mechanice gry nie powinna wpłynąć na testy.

Ale jednak wyobrażam sobie problemy.

Po pierwsze: co chyba najbardziej wymaga przetestowania to jest to, czy jeśli na potworku jest na raz wiele różnych efektów / zakolejkowanych akcji, to czy one adekwatnie ze sobą współdziałają i czy nie ma bugów wywołanych jakimiś interakcjami. Szybko się okazało, że przy obliczaniu pojedynczej tury kolejność akywacji efektów ma znaczenie: prosty przykład, jeśli potworek ma wykonać akcję kosztującą Staminę a jednocześnie jest pod wpływem damage over time który zjada mu Staminę to ma znaczenie, czy najpierw zużyjemy staminę dla ruchu czy dla damage over time. W tym celu efektom dałem defaultPriority, ale ten defaultPriority jest nierzadko nadpisywany... np mamy akcję Channel, która po odczekaniu odp ilości tur nakłada na potworka inną akcję (np. Fireball). W tym celu Channel ma bardzo wysoką Priority, no bo musi zdążyć aktywować powierzone jej akcje do momentu, w którym w turze jest wymiana ciosów. No ale teraz mamy ruch Withdraw, który po odczekaniu odpowiedniej ilości tur wymienia potworka z innym... koniecznie już PO wymianie ciosów (sensem i celem tego ruchu jest umożliwienie potworkowi, na którego się wymienia, bezpiecznego wejścia). Więc trzeba nadpisać priorytety.

Tyle że akcji jest n, ruchów jest m, a jak wiadomo możliwych kombinacji jest wykładniczo wiele... W ten sposób liczba testów stałaby się niepraktyczna! Oczywiście, napisanie tych wszystkich testów zmusiłoby mnie do rozważenia wszystkich corner case'ów (na razie obawiam się mogą być bugi wynikłe z nie dość dokładnego zaprojektowania, tj nie tak że kod robi coś innego niż ja miałem intencję, by robił, tylko tak, że ja nawet w moich intencjach nie zauważyłem jakiegoś corner case'u i nie ogarnąłem, co się wtedy POWINNO dziać). Niemniej, wykładnicza ilość kombinacji jest nie do utrzymania i nie do zapisania.

Drugi problem: Gra jednak zawiera elementy losowości. Jak zatem przetestować te elementy, które zależą od losowości? Np. ruch Dodge, który daje % szansy na uniknięcie ciosu. Jedyny pomysł jaki mi przychodzi do głowy to stworzyć Randoma z zadanym seedem i na podstawie odgórnej znajomości wyników jakie ten Random wypluje przetestować czy rzeczywiście jest unik gdy Random wypluwa takie liczby i czy uniku nie ma gdy Random wypluwa inne liczby. No ale to znowu ściśle wiąże testy z implementacją, czego miało nie być. Jeśli np gdzieś są 2 losowania i odwrócimy kolejność tych losowań to już trzeba poprawiać multum testów.

Komentarze?

0

W kwestii testowania losowości IMO najczystszym rozwiązaniem byłoby pominięcie losowania i ręczne ustawianie jego wyników. Czyli powiedzmy tworzysz obiekt Dodge ustawiając mu właściwość w rodzaju IsEffective i taki obiekt przekazujesz testowanej logice. W ten sposób rozwiązujesz problem kolejności losowań.
Sam mam niewielkie doświadczenie z testowaniem i chętnie poczytam inne opinie na ten temat :)

0

Ja tu nie widzę problemu z implementacją testów, ale z tym, że najwyraźniej nawet autor gry nie zna jej zasad.

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