Strategia testowania
Aplikację Archestack testuje się warstwami, od edytora, który zaznacza błąd w trakcie pisania, po pipeline, który niczego nie wdroży, dopóki któryś test nie przechodzi. Same testy są częścią aplikacji: znajdują się w jej packages, podróżują z nią do każdej przestrzeni roboczej i działają na własnej bazie sandbox, więc test nigdy nie czyta ani nie zmienia niczyich prawdziwych danych.
Pięć warstw
| Warstwa | Co wychwytuje | Kiedy działa |
|---|---|---|
| Edytor | Kod C# i kod stron, który się nie kompiluje, kolumnę lub Business Entity, które nie istnieją, Script Module wywołany z błędnym argumentem | W trakcie pisania |
| Uruchomienia próbne | Skrypt lub Business Event, który robi coś nie tak, wypróbowany przed zapisaniem | Podczas pisania skryptu |
| Przypadki testowe | Zachowanie: regułę, którą egzekwuje Business Event, to, co zwraca Script Module, to, co odczytuje Business Entity | Przy każdej zmianie i przed każdym wdrożeniem |
| Sprawdzenia schematu | Zmianę tabeli, która traci dane albo przez którą typowany skrypt przestałby się kompilować | Przed każdą migracją i każdym wdrożeniem |
| Zestawy według harmonogramu | Wszystko, co zmieniło się wokół aplikacji: inny package, aktualizację, dane, których reguła się nie spodziewała | Co noc albo według dowolnego harmonogramu, w każdym środowisku |
Dwie pierwsze nie wymagają pracy: edytor sprawdza kod tym samym kompilatorem i tym samym modelem
danych, z którymi działa aplikacja, a przycisk Test Script Module i symulacja
Business Event działają w trybie próbnym, w którym zapisy przez Db i
Files są sprawdzane i rejestrowane, ale nigdy nie wykonywane, a maile i powiadomienia
są rejestrowane, ale nie wysyłane. Wszystko inne, co skrypt robi sam, dzieje się naprawdę:
wywołania HTTP, System.IO i wszystko, co robi
pakiet NuGet, na przykład połączenie z inną bazą
danych. Reszta tej strony dotyczy trzeciej
warstwy, testów, które piszesz, i tego, jak uruchamiają je dwie pozostałe.
Przypadki testowe: given, when, then
Przypadek testowy to jedno twierdzenie o aplikacji, w trzech częściach: wiersze, które trzeba przygotować (given), jedna rzecz do uruchomienia (when) i to, co potem musi być prawdą (then). Przypadki są grupowane w zestawy. Ten, z aplikacji demo Service Planner, dowodzi, że tablica planowania oznacza dzień jako przepełniony tylko wtedy, gdy zaplanowane godziny technika przekraczają jego limit godzin:
{
"name": "Week load flags a day over capacity",
"suite": "Service Planner",
"isEnabled": true,
"data": {
"description": "A day is overbooked only when its planned hours go over the technician's capacity.",
"given": [
{ "table": "service_technician", "rows": [
{ "name": "Ana Peeters", "daily_capacity_hours": 8 },
{ "name": "Bram Maes", "daily_capacity_hours": 8 }
] },
{ "table": "service_job", "rows": [
{ "title": "Heat pump install", "technician_id": 1, "planned_date": "2026-09-23", "estimated_hours": 5, "status": "Planned" },
{ "title": "Boiler service", "technician_id": 1, "planned_date": "2026-09-23", "estimated_hours": 4, "status": "Planned" },
{ "title": "Commissioning", "technician_id": 2, "planned_date": "2026-09-23", "estimated_hours": 8, "status": "Planned" }
] }
],
"when": { "kind": "ScriptModule", "name": "GetPlannerWeek", "parameters": { "week_start": "2026-09-21" } },
"then": [
{ "subject": "Result", "operator": "Succeeds" },
{ "subject": "Result", "operator": "Equals", "path": "technicians[0].days[2].overbooked", "expected": "true" },
{ "subject": "Result", "operator": "Equals", "path": "technicians[1].days[2].overbooked", "expected": "false" }
]
}
} Given
Wiersze, których przypadek potrzebuje, tabela po tabeli, zapisywane od góry do dołu do pustego
sandboxa. Wiersze dostają identyfikatory 1, 2, 3 w kolejności zapisu i to na nie wskazuje klucz
obcy późniejszego fixture'a: powyżej technician_id 1 to Ana. Dlatego tabela, na którą
wskazuje inny fixture, idzie pierwsza, a klucz generowany przez bazę danych jest pomijany,
podobnie jak kolumny audytowe i każda kolumna, której wartość domyślna wystarczy. W builderze
wybranie tabeli wypełnia przykładowe wiersze na podstawie nazw i typów jej kolumn, a klucz obcy
jest listą wyboru spośród wierszy zapisywanych przez fixtures powyżej.
When
Dokładnie jeden cel, wskazany nazwą, nigdy identyfikatorem:
- Script Module: uruchamia moduł z podanymi parametrami.
- Business Entity: odczytuje encję, opcjonalnie z filtrem, żeby sprawdzić, co pokazałaby strona.
- Business Event: odpala trigger na wierszu, we wskazanym momencie (BeforeCreate, AfterUpdate i tak dalej), tak jak zrobiłby to zapis.
- Script: dowolny kod C# ze wszystkim, co ma Script Module, dla przypadku, który musi zrobić kilka rzeczy, zanim da się sprawdzić twierdzenie.
Then
Każde sprawdzenie to przedmiot sprawdzenia, operator i zwykle wartość oczekiwana. Przedmioty to
Result (to, co zwrócił cel), Rows (wiersze odczytane potem z tabeli),
Output (to, co cel zapisał w logu), Error, Duration i Code.
Operatory to Equals, NotEquals, Contains, NotContains, GreaterThan, LessThan, IsNull, IsNotNull,
Matches (wyrażenie regularne), RowCount, Succeeds i Fails. Ścieżka taka jak
technicians[0].days[2].overbooked sięga w głąb wyniku, a ścieżka, która prowadzi
donikąd, sprawia, że sprawdzenie kończy się niepowodzeniem, i mówi, gdzie się zatrzymała.
Testowanie, że coś zostaje odrzucone
Wiele reguł aplikacji mówi nie: Business Event odrzuca zapis, który by coś zepsuł. Przypadek dla takiej reguły próbuje zapisać i sprawdza, że zapis został odrzucony, z właściwego powodu, i że nic nie zostało zapisane:
// A zero estimate is refused.csx: the target, a save the business event must stop
await Db.CreateAsync("service_job", new { title = "Check radiator valves", estimated_hours = 0 });
return "saved"; "then": [
{ "subject": "Result", "operator": "Fails" },
{ "subject": "Error", "operator": "Contains", "expected": "Estimated hours must be more than zero" },
{ "subject": "Rows", "operator": "RowCount", "table": "service_job", "expected": "0" }
] Liczy się drugie sprawdzenie. Samo Fails przechodzi przy każdym błędzie, także przy literówce w nazwie kolumny, a to najczęstszy sposób, w jaki czerwony test po cichu przestaje cokolwiek testować. Powiąż niepowodzenie z jego komunikatem.
Sprawdzenia w kodzie
Sprawdzenie, którego nie da się wyrazić jednym przedmiotem sprawdzenia i jednym operatorem, jest
sprawdzeniem w kodzie: kodem C#, który działa po celu w tym samym sandboxie, ze
wszystkim, co ma Script Module, łącznie z typowanymi zbiorami w Db.
Result, Output, Error, DurationMs i
Failed przechowują wynik celu, a Assert, gdy sprawdzenie zawiedzie, mówi,
czego oczekiwał i co zastał (Equal, NotEqual, True, False, Null, NotNull, Contains, Count i Fail).
Sprawdzenie kończy się niepowodzeniem, gdy rzuci wyjątek, nie przejdzie Assert albo zwróci false.
// Planning a job marks it Planned.then.1.csx: a check one subject and one operator cannot say
var job = await Db.ServiceJob.SingleAsync(j => j.Title == "Boiler service");
Assert.Equal("Planned", job.Status);
Assert.NotNull(job.PlannedDate, "a planned job has a date");
var sameDay = await Db.ServiceJob
.Where(j => j.TechnicianId == job.TechnicianId && j.PlannedDate == job.PlannedDate)
.SumAsync(j => j.EstimatedHours);
Assert.True(sameDay <= 8, $"the day holds {sameDay} hours");
Sięgnij po nie, gdy sprawdzenie potrzebuje kilku wierszy naraz, obliczeń na nich albo porównania
dwóch tabel. W repozytorium każde sprawdzenie w kodzie jest osobnym plikiem,
<Case>.then.<n>.csx, edytowanym z tymi samymi funkcjami C# co każdy
skrypt.
Sandbox
Test nigdy nie dotyka danych przestrzeni roboczej. Uruchomienie przekierowuje bazę danych z danymi do sandboxa, osobnej bazy danych tworzonej na żądanie na wzór prawdziwej, a każda tabela w nim jest opróżniana przed każdym przypadkiem, więc jedyne wiersze, które widzi przypadek, to jego własne fixtures i to, co zapisze jego cel. Definicje nadal pochodzą z przestrzeni roboczej, więc test sprawdza te Business Entities, Business Events i skrypty, które mają zaraz zostać wydane. Każda przestrzeń robocza ma cztery sandboxy, więc cztery uruchomienia mogą działać naraz; piąte czeka, aż któryś się zwolni.
- Maile i powiadomienia są rejestrowane, a nie wysyłane. Wynik każdego przypadku wymienia, co jego skrypty wysłałyby mailem i kogo by powiadomiły, więc przypadek może sprawdzić także to.
- Pliki zostają bez zmian. Zapisy, przeniesienia i usunięcia przez
Filesnigdy nie docierają do folderu workspace'u: przebieg zatrzymuje je dla siebie, więc odczyt właśnie zapisanego pliku działa. - Połączenia na zewnątrz są prawdziwe. Wszystko inne, co skrypt robi sam, dzieje się przy każdym uruchomieniu: żądanie HTTP, połączenie SMTP, które otwiera,
System.IOi wszystko, co robi pakiet NuGet, na przykład połączenie z inną bazą danych, łącznie z zapisami. Trzymaj wywołania innych systemów z dala od tego, co uruchamiają testy, albo kieruj je na testowy endpoint lub testową bazę danych. - Ewidencja jest prawdziwa. Uruchomienie zapisanego przypadku odnotowuje wynik w przypadku i w zestawie oraz tworzy wpis w Event Log. Uruchomienie niezapisanego szkicu, przyciskiem Run w edytorze przypadku albo z VS Code, niczego nie odnotowuje.
Gdzie znajdują się testy
Test należy do package'a, który testuje. Podróżuje z tym package'em przez eksport, import albo
Module Store, więc moduł trafia w nowe miejsce z dowodem, że nadal działa, a wszystko, na co
wskazuje (Script Module, Business Entity, Business Event), wskazuje nazwą, więc trafia we właściwą
rzecz w każdej przestrzeni roboczej. W repozytorium testy package'a są plikami:
tests/<suite>/suite.json, jeden plik JSON na przypadek w cases/ i
plik .csx obok przypadku dla celu Script albo sprawdzenia w kodzie. Przechodzą przegląd
i scalanie tak jak kod, który testują.
Uruchamianie testów
- W builderze strona Test Suite wymienia zestawy i ich przypadki i uruchamia przypadek, zestaw albo wszystko, z wartością oczekiwaną wobec rzeczywistej dla każdego nieudanego sprawdzenia. Run w edytorze przypadku wypróbowuje przypadek, zanim zostanie zapisany.
- W VS Code Run Tests uruchamia przypadek, zestaw, package albo wszystkie packages na projekcie, po cztery przypadki naraz, i pokazuje każdy wynik w drzewie i w widoku Testing. Zobacz Testy w przewodniku po VS Code.
- Według harmonogramu zestaw z cronem uruchamia się sam, w każdym środowisku, w
którym jest zainstalowany:
0 0 2 * * ?to każdej nocy o drugiej. Każde uruchomienie tworzy jeden wpis w Event Log ze szczegółami każdego sprawdzenia, a jego ostatni wynik widać przy zestawie. - W pipeline'ie testy wszystkich packages działają na tymczasowym Archestack zbudowanym z repozytorium, zanim cokolwiek zostanie wdrożone, a jedno niepowodzenie zatrzymuje wdrożenie. Zobacz Strategia wdrożeń.
- Z asystenta AI: MCP Server daje podłączonemu modelowi ten sam przewodnik po testach i narzędzia do napisania przypadku, uruchomienia go i poprawiania, aż przejdzie.
Jak czytać wynik
| Wynik | Znaczenie |
|---|---|
| Passed | Każde sprawdzenie przeszło. |
| Failed | Cel został uruchomiony, a jedno ze sprawdzeń nie przeszło. Każde nieudane sprawdzenie mówi, czego oczekiwało i co zastało. |
| Error | Przypadek nie doszedł do swoich sprawdzeń: fixture nie dał się wstawić, cel ma błędną nazwę albo cel rzucił wyjątek, choć przypadek się tego nie spodziewał. Najpierw przeczytaj błąd. |
| Skipped | Przypadek albo jego zestaw jest wyłączony. |
Co testować najpierw
- Business Events, które chronią dane. Każda reguła, która odrzuca zapis albo usunięcie, dostaje przypadek, który zostaje odrzucony, i przypadek, który zostaje dopuszczony, więc wiadomo, że reguła istnieje i nie jest zbyt surowa.
- Script Modules, które liczą. Mając kilka wierszy, sprawdź liczby, które zwraca moduł, i przypadki brzegowe: pusty dzień, dzień wypełniony dokładnie, dzień przepełniony.
- Odczyty, od których zależy aplikacja. Cel Business Entity z filtrem i RowCount dowodzą, że strona pokazuje wiersze, które powinna, i żadnego, którego nie powinna.
- Przepływy z kilkoma krokami. Cel Script, który robi to, co zrobiłby użytkownik, i sprawdzenia w kodzie wszystkiego, co powinno z tego wyniknąć.
Testy, które pozostają przydatne
- Jeden przypadek, jedno twierdzenie. Przypadek, który sprawdza dziesięć rzeczy, nie mówi, która z dziesięciu się zepsuła.
- Nazwij przypadek od reguły. "A zero estimate is refused" czyta się na liście wyników jak specyfikację.
- Sprawdzaj zachowanie, nie fixture. Liczenie wierszy, które właśnie wstawiłeś, testuje mechanizm ładowania fixtures.
- Wiąż każde niepowodzenie z jego komunikatem, jak wyżej.
- Utrzymuj małe fixtures. Tylko wiersze potrzebne twierdzeniu, bez generowanych kluczy.
- Trzymaj testy razem z kodem. Zmiana i test, który jej dowodzi, należą do tego samego package'a i do tego samego pull requesta.
Co dalej
Strona Archestack w VS Code opisuje uruchamianie testów z edytora, Strategia wdrożeń pipeline, który uruchamia je przed każdym wdrożeniem, a strona Reference Business Events i Script Modules, których dotyczy większość testów.