Technologia · Unity

1541 z moich
1624 testów gry
nigdy nie uruchamia gry.

To nie jest poradnik o Unity Test Framework. To decyzja podjęta długo przed powstaniem pierwszego testu, która po cichu rozstrzyga, czy będziesz w stanie napisać drugi tysiąc.

Marcin Firmuga·2026-10-05·9 min czytania·Technologia

Każdy tutorial o testowaniu w Unity pokazuje, jak napisać test. Prawie żaden nie mówi, dlaczego twoja gra będzie się przy tym opierać — a ta część to architektura, nie narzędzia.

Oto kształt zestawu, który się nie opierał, zmierzony, a nie zapamiętany:

GdziePlikówTestówWymaga działającej gry
Edit Mode1791541Nie
Play Mode1883Tak

Dziewięćdziesiąt pięć procent z nich działa bez sceny, bez MonoBehaviour i bez klatki. Ta proporcja nie jest dyscypliną ani upodobaniem. Jest bezpośrednią konsekwencją jednej reguły, a ta reguła to cały ten artykuł.

Zakres. Pisane z symulacji zarządzania, gdzie stan kumuluje się przez cztery do pięciu lat gry. Jeśli twoja gra to krótkie, autorskie doświadczenie z ręcznie układaną zawartością, przeczytaj najpierw ostatnią sekcję — uczciwa odpowiedź jest tam inna.

Jeden obiekt może zmieniać stan. To jest ta reguła.

Tym, co czyni grę testowalną, nie jest framework, biblioteka do mocków ani wstrzykiwanie zależności. Jest nim to, że istnieje dokładnie jedno miejsce, w którym świat może się zmienić, i przechodzi przez nie wszystko: każdy przycisk, każdy tik, każdy system.

W moim projekcie ten obiekt nosi taki komentarz i jest to najbardziej nośny akapit w całej bazie kodu:

/// JEDYNA rzecz uprawniona do zmiany CompanyState. Przechodzi przez to każda akcja gracza /// i cały dzienny tik, i dlatego test może przegrać cztery lata historii firmy bez sceny, /// bez MonoBehaviour i bez klatki. /// /// Dzień, po kolei: dostawy lądują, trening zjada moc, rynek dzieli popyt, wychodzą rachunki, /// bramki są sprawdzane ponownie. Nic w tej kolejności nie podlega negocjacji, bo klaster, /// który przyjeżdża rano, powinien serwować tokeny tego samego popołudnia.

Przeczytaj twierdzenie przyczynowe w środku: i dlatego test może przegrać cztery lata. Testowalność nie jest funkcją, którą ktoś zbudował. Jest tym, co dostajesz za darmo, gdy nic nie może zmienić świata za plecami tej bramy.

Drugi akapit robi cichszą robotę. Ustalona, udokumentowana kolejność operacji oznacza, że test może sprawdzać stan po dowolnym dniu i dostawać tę samą odpowiedź za każdym razem — z tego samego powodu, dla którego kampania w ogóle jest powtarzalna, czyli z tematu artykułu o deterministycznym losowaniu. Kolejność i determinizm to ten sam problem w dwóch kapeluszach.

Pomiar, który mówi, czy to masz

Jest jednolinijkowy sprawdzian na to, czy twoja logika gry jest testowalna, i jest uczciwszy niż jakiekolwiek deklaracje. Policz, ile plików twojej symulacji importuje silnik:

grep -rl "using UnityEngine" --include=*.cs Assets/TwojaGra/Scripts/Simulation | wc -l

U mnie odpowiedź brzmi 0, na 98 plikach. Nie dlatego, że importowanie silnika jest zakazane jakąś regułą, którą egzekwuję, tylko dlatego, że gdy symulacja operuje na własnym stanie wyłącznie przez jedną bramę, nigdy nie ma powodu sięgać po Vector3, Debug.Log czy Time.deltaTime. Silnik okazuje się rzeczą, której logika nigdy nie potrzebowała.

Jeśli twoja liczba jest duża, to właśnie jest ta robota. Każde using UnityEngine w pliku z regułami to mały zakład o to, że ta reguła zawsze będzie działać wewnątrz gry — i to ten zakład sprawia później, że czteroletniej kampanii nie da się przetestować w sekundę.

Dwa typy z silnika, które wchodzą niepostrzeżenie i kosztują najwięcej

Time.deltaTime czyni liczbę klatek wejściem do symulacji, więc ta sama kampania na szybszej maszynie jest inną kampanią. Symulacja ma posuwać się w jednostkach, które sama posiada — dzień, tik, tura — a warstwa prezentacji niech decyduje, jak szybko ją wywoływać.

Vector3 i spółka wyglądają niewinnie i wciągają za sobą cały assembly silnika. Jeśli twoja logika naprawdę potrzebuje współrzędnych, czterolinijkowa własna struktura utrzymuje granicę czystą i nie kosztuje nic.

Assembly definitions to część, która tego pilnuje

Reguła, której nikt nie złamie przez przypadek, jest warta więcej niż reguła, z którą wszyscy się zgadzają. Assembly definitions są sposobem, w jaki Unity ci to daje, i są krokiem pomijanym przez większość tutoriali, bo przykład-zabawka ich nie potrzebuje.

W moim projekcie robią to cztery pliki:

AssemblyDo czego służy
ScalingLaws.RuntimeSama gra: symulacja, dane, zapisy, interfejs.
ScalingLaws.EditorNarzędzia wyłącznie edytorowe, które nigdy nie mogą trafić do builda gracza.
ScalingLaws.Tests.EditModeTe 1541. Bez sceny, bez klatki.
ScalingLaws.Tests.PlayModeTe 83, które naprawdę potrzebują silnika.

Plik asmdef testów wart jest przeczytania linijka po linijce, bo trzy jego pola to te, które ludzie ustawiają źle:

{ "name": "ScalingLaws.Tests.EditMode", "references": [ "ScalingLaws.Runtime", "ScalingLaws.Editor" ], "includePlatforms": [ "Editor" ], "autoReferenced": false, "defineConstraints": [ "UNITY_INCLUDE_TESTS" ], "optionalUnityReferences": [ "TestAssemblies" ] }

Cichą korzyścią jest lista referencji. Ponieważ assembly testowe deklaruje dokładnie, co widzi, błąd architektoniczny — na przykład plik symulacji sięgający do interfejsu — przestaje być kwestią code review, a staje się błędem kompilacji.

Do czego służą pozostałe osiemdziesiąt trzy testy

Artykuł byłby schludniejszy, gdyby odpowiedź brzmiała „do niczego, wrzuć wszystko do Edit Mode". Jest błędna, a te osiemdziesiąt trzy to miejsce, gdzie mieszka konkretna klasa błędów.

Testy Play Mode mają pokrywać wszystko, czego tematem jest silnik: ładowanie scen, podpięcie prefabów, interfejs istniejący dopiero po wyrenderowaniu dokumentu, korutyny, fizykę. Ale kategoria, którą ludzie pomijają, jest tą ważną — testy dowodzące, że rdzeń bez sceny jest faktycznie podłączony do tego, co widzi gracz.

Dlaczego to waży więcej, niż brzmi

Symulacja z 1541 przechodzącymi testami nadal może wydać ekran, który otwiera się pusty. Logika jest udowodniona; podłączenie nie. Każdy błąd tego kształtu, jaki miałem, wygląda od środka tak samo: jeden stan, dwa miejsca, które go ustawiają, i tylko jedno zawiadamia ekran.

Edit Mode nie może tego zobaczyć z definicji, bo tam ekran nie istnieje. Jeśli napiszesz tylko trzy testy Play Mode, niech to będą: „każdy ekran otwiera się i coś pokazuje pierwszego dnia", „zmiana w symulacji dociera do ekranu" i „kliknięcie na ekranie dociera do symulacji".

Czym się te 1541 testów okazało

„Tysiąc sześćset testów" brzmi jak liczba zaprojektowana, żeby robić wrażenie, i sama w sobie by nią była. Pożyteczniej rozbić ją na cztery zadania, które wykonują, bo czwarte ma prawie nikt:

RodzajNa jakie pytanie odpowiada
Testy reguł Czy to jedno obliczenie robi to, co deklaruje? Zwyczajna większość i najtańsze w pisaniu.
Testy spójności Czy zawartość jest spójna? Że każdy wymóg badań istnieje, że drzewo nie ma cykli, że każda zablokowana rzecz ma dokładnie jedną bramkę. Czytają katalogi, a nie kod, i wyłapują pomyłki, które robisz o drugiej w nocy w pliku z danymi.
Testy zapisu Czy zapis przeżywa podróż w obie strony i czy stary zapis nadal się wczytuje? Każdy z osobna tani; razem są powodem, dla którego kampania gracza sprzed trzech miesięcy wciąż się otwiera.
Testy grywalności Czy to nadal jest gra? Skryptowany bot rozgrywa pełne cztery lata, a asercje opisują przedział trudności. Piętnaście testów, opisanych w osobnym artykule, i znalazły błędy, których żaden test jednostkowy nie mógł zobaczyć.

Warstwę spójności dodałbym w cudzym projekcie jako pierwszą. Test, który przechodzi twoją zawartość i sprawdza, że jest poprawnie zbudowana, kosztuje godzinę i nigdy nie przestaje się zwracać, bo to w zawartości solowy twórca robi najwięcej pomyłek i ma najmniej kontroli.

Ile to kosztuje i kiedy się nie opłaca

Pisałbym reklamę, a nie poradnik, gdybym skończył w tym miejscu.

Prawdziwym kosztem nie jest pisanie testów. Jest nim to, że architekturę trzeba ustalić wcześnie, a jej dorobienie później jest drogie. Oddzielenie symulacji od silnika po fakcie oznacza znalezienie każdego miejsca, gdzie reguła czyta Time.deltaTime albo zahacza o MonoBehaviour, a w projekcie dowolnej wielkości to tygodnie, nie popołudnie. Opisałem jedyny raz, kiedy musiałem to zrobić, i nie było przyjemnie.

Drugim kosztem jest to, że rdzeń bez sceny jest trochę mniej wygodnym sposobem pisania gry. Nie przeciągniesz wartości na komponent w inspektorze tak, żeby symulacja ją zobaczyła. Wszystko jest jawne, co jest lepsze w dwunastym miesiącu i oznacza więcej pisania w pierwszym tygodniu.

Uczciwy argument przeciwko temu wszystkiemu

Jeśli twoja gra jest krótka, autorska i ręcznie układana — gra narracyjna, logiczna z projektowanymi poziomami, platformówka, w której zawartością są sceny — większość tego kupuje ci znacznie mniej. Nie ma czteroletniej kampanii do symulowania ani kumulującej się ekonomii do ochrony. Te same godziny wydane na poziomy dadzą lepszą grę.

Sprawdzian, który to rozstrzyga: czy człowiek jest w stanie przechodzić całą twoją grę na tyle często, żeby zauważyć regresję? Jeśli tak, graj. Jeśli pełne przejście to cztery lata gry i nikt tego nigdy nie zrobi ręcznie po każdej zmianie, zestaw testów nie jest rytuałem jakości — jest jedynym przyrządem, jaki masz do obserwowania tego, co zbudowałeś.

Kolejność, w której zrobiłbym to ponownie

Pytania, które ludzie zadają

Czy da się testować logikę gry w Unity bez wchodzenia w Play Mode?

Tak i większość powinna tak działać. Dokumentacja Unity oddziela testy Edit Mode, działające w edytorze bez pętli gry, od testów Play Mode, które uruchamiają grę; Edit Mode używa zwykłego atrybutu [Test] z NUnit i chodzi znacznie szybciej. W mojej wydanej symulacji podział to 1541 wobec 83, czyli około dziewięćdziesiąt pięć procent zestawu nigdy nie uruchamia gry. Ograniczeniem nie jest framework, tylko to, czy twoja logika w ogóle potrafi działać bez sceny.

Co w ogóle czyni logikę gry testowalną?

Jedna reguła podjęta wcześnie: dokładnie jeden obiekt może zmieniać stan gry i przechodzi przez niego każda akcja gracza oraz każdy tik. Gdy to obowiązuje, test może zbudować stan, wywołać bramę i krokować dalej bez sceny, bez MonoBehaviour i bez klatki, bo nic innego nie mogło nic zmienić za jej plecami. Te 98 plików symulacji importujących UnityEngine zero razy to konsekwencja tej reguły, a nie osobny cel.

Co powinno zostać w testach Play Mode?

Wszystko, czego tematem naprawdę jest silnik: ładowanie scen, podpięcie prefabów, interfejs istniejący dopiero po wyrenderowaniu dokumentu, korutyny, fizyka. Plus kategoria pomijana przez większość — sprawdzenia dowodzące, że rdzeń bez sceny jest naprawdę podłączony do tego, co widzi gracz. To pominięcie jest powodem, dla którego gra może mieć tysiące zielonych testów i ekran otwierający się pusty.

Czy do testowania kodu Unity potrzebne są assembly definitions?

Do czegokolwiek poza zabawką tak. Assembly testowe ustawia includePlatforms wyłącznie na Editor, dodaje TestAssemblies do opcjonalnych referencji Unity i ogranicza się przez UNITY_INCLUDE_TESTS, żeby kompilator pominął je w buildzie gracza. Wymuszają też architekturę: referencja, której nie zadeklarowałeś, staje się błędem kompilacji zamiast cichą zależnością.

Jak długo chodzi 1624 testów?

Większość w Edit Mode jest szybka, bo nie ma pętli gry, na którą trzeba czekać; drogie są te kilkanaście, które symulują cztery albo pięć lat gry, i to one dominują czas całkowity. Praktyczne ustawienie to uruchamiać wszystko na commicie, a długie testy grywalności trzymać poza pętlą, którą odpalasz co trzydzieści sekund przy edytowaniu reguły.

Czy warto testować solowy projekt gry?

To zależy od gry. Przy symulacji albo strategii, gdzie liczby kumulują się przez godziny, testy są jedynym sposobem obserwowania tego, co budujesz, bo nikt nie rozegra czterech lat gry ręcznie po każdej zmianie. Przy krótkim, autorskim doświadczeniu ten sam wysiłek kupuje znacznie mniej i uczciwa odpowiedź brzmi: wydaj go na poziomy.

Liczby w tym artykule są policzone, nie zapamiętane. Assembly definitions, folder symulacji i cały zestaw testów są w publicznym repozytorium Scaling Laws, podlinkowanym w źródłach. Grep z drugiej sekcji to ten sam, który uruchomiłem, żeby to napisać.
Dalej w tej serii: ekonomia tycoona jako prawa · dlaczego solowe projekty umierają na tych samych czterech funkcjach · testy balansu, które dowodzą, że grę da się wygrać.
MF

Marcin Firmuga

Solo developer · HCK_Labs · buduję publicznie

Piszę o tym, co naprawdę wypuściłem, z prawdziwymi liczbami i prawdziwym kodem, łącznie z tym, co nie zadziałało. Więcej: moja historia.