Testowanie z nf-test¶
Tłumaczenie wspomagane przez AI - dowiedz się więcej i zasugeruj ulepszenia
Możliwość systematycznego sprawdzania, czy każda część workflow'u działa zgodnie z oczekiwaniami, jest kluczowa dla reprodukowalności i długoterminowego utrzymania kodu — a podczas samego procesu tworzenia może być ogromnym ułatwieniem.
Poświęćmy chwilę na omówienie, dlaczego testowanie jest tak ważne. Tworząc workflow, jedną z pierwszych rzeczy, które robisz, jest zebranie danych testowych, o których wiesz, że są poprawne i powinny dać określony wynik. Dodajesz pierwszy proces do pipeline'u i podłączasz go do wejść, żeby działał. Następnie, żeby sprawdzić, czy wszystko gra, uruchamiasz go na danych testowych. Zakładając, że to działa, przechodzisz do kolejnego procesu i znowu uruchamiasz dane testowe. Powtarzasz ten cykl, aż uzyskasz pipeline, z którego jesteś zadowolony.
Potem może dodajesz prosty parametr logiczny, np. --skip_process. Teraz musisz uruchomić pipeline dwukrotnie — raz z każdym parametrem — żeby upewnić się, że działa zgodnie z oczekiwaniami. Ale chwila, jak sprawdzić, czy --skip_process rzeczywiście pomija dany proces? Trzeba zagłębić się w wyjścia albo przejrzeć pliki logów! To uciążliwe i podatne na błędy.
W miarę rozwijania pipeline'u szybko stanie się on na tyle złożony, że ręczne testowanie każdej iteracji będzie powolne i zawodne. Co więcej, jeśli znajdziesz błąd, bardzo trudno będzie ustalić, w którym dokładnie miejscu pipeline'u on powstaje. Tu właśnie wkracza testowanie.
Testowanie pozwala systematycznie sprawdzać, czy każda część pipeline'u działa zgodnie z oczekiwaniami. Korzyści z dobrze napisanych testów są dla programisty ogromne:
- Pewność: Ponieważ testy obejmują cały pipeline, możesz być pewny, że zmiana jednej rzeczy nie wpłynie na pozostałe.
- Zaufanie: Gdy nad pipeline'em pracuje wielu programistów, każdy wie, że inni nie zepsuli pipeline'u ani żadnego z jego komponentów.
- Przejrzystość: Testy wskazują, gdzie pipeline zawodzi, i ułatwiają namierzenie problemu. Pełnią też rolę dokumentacji, pokazując, jak uruchomić dany proces lub workflow.
- Szybkość: Ponieważ testy są zautomatyzowane, można je uruchamiać bardzo szybko i wielokrotnie. Możesz iterować sprawnie, z mniejszą obawą o wprowadzanie nowych błędów.
Istnieje wiele różnych rodzajów testów, które możemy pisać:
- Testy na poziomie modułu: Dla poszczególnych procesów
- Testy na poziomie workflow'u: Dla pojedynczego workflow'u
- Testy na poziomie pipeline'u: Dla całego pipeline'u
- Testy wydajnościowe: Dla szybkości i efektywności pipeline'u
- Testy obciążeniowe: Ocena działania pipeline'u w ekstremalnych warunkach w celu określenia jego limitów
Testowanie poszczególnych procesów jest analogiczne do testów jednostkowych w innych językach. Testowanie workflow'u lub całego pipeline'u odpowiada temu, co w innych językach nazywa się testami integracyjnymi — sprawdzamy w nich interakcje między komponentami.
nf-test to narzędzie umożliwiające pisanie testów na poziomie modułu, workflow'u i pipeline'u. Krótko mówiąc, pozwala systematycznie sprawdzać, czy każda indywidualna część pipeline'u działa zgodnie z oczekiwaniami — w izolacji.
Cele szkolenia¶
W tym zadaniu dodatkowym nauczysz się używać nf-test do pisania testu na poziomie workflow'u dla pipeline'u, a także testów na poziomie modułu dla trzech wywoływanych przez niego procesów.
Po ukończeniu tego zadania będziesz potrafić efektywnie stosować następujące techniki:
- Inicjalizować nf-test w swoim projekcie
- Generować testy na poziomie modułu i workflow'u
- Dodawać typowe rodzaje asercji
- Rozumieć, kiedy używać snapshotów, a kiedy asercji treści
- Uruchamiać testy dla całego projektu
Te umiejętności pomogą Ci wdrożyć kompleksową strategię testowania w projektach pipeline'owych, czyniąc je bardziej niezawodnymi i łatwymi w utrzymaniu.
Wymagania wstępne¶
Przed przystąpieniem do tego zadania dodatkowego powinieneś:
- Ukończyć samouczek Hello Nextflow lub równoważny kurs dla początkujących.
- Swobodnie posługiwać się podstawowymi konceptami i mechanizmami Nextflow (procesy, kanały, operatory, praca z plikami, metadane).
0. Pierwsze kroki¶
Otwórz środowisko szkoleniowe¶
Jeśli jeszcze tego nie zrobiłeś, otwórz środowisko szkoleniowe zgodnie z opisem w sekcji Konfiguracja środowiska.
Przejdź do katalogu projektu¶
Przejdźmy do katalogu, w którym znajdują się pliki tego samouczka.
Możesz ustawić VSCode tak, żeby skupiał się na tym katalogu:
Przejrzyj materiały¶
Znajdziesz tu główny plik workflow'u oraz plik CSV o nazwie greetings.csv, zawierający dane wejściowe do pipeline'u.
Szczegółowy opis plików znajdziesz w rozgrzewce z Hello Nextflow.
Workflow, który będziemy testować, jest podzbiorem workflow'u Hello zbudowanego w Hello Workflow.
Co robi workflow Hello Nextflow?
Jeśli nie przechodziłeś szkolenia Hello Nextflow, oto krótki przegląd tego, co robi ten prosty workflow.
Workflow przyjmuje plik CSV zawierający pozdrowienia, przepuszcza je przez cztery kolejne kroki transformacji i zwraca pojedynczy plik tekstowy zawierający obrazek ASCII z zabawną postacią wypowiadającą te pozdrowienia.
Cztery kroki są zaimplementowane jako procesy Nextflow (sayHello, convertToUpper, collectGreetings i cowpy) przechowywane w osobnych plikach modułów.
sayHello: Zapisuje każde pozdrowienie do własnego pliku wyjściowego (np. "Hello-output.txt")convertToUpper: Konwertuje każde pozdrowienie na wielkie litery (np. "HELLO")collectGreetings: Zbiera wszystkie pozdrowienia pisane wielkimi literami do jednego pliku zbiorczegocowpy: Generuje grafikę ASCII przy użyciu narzędziacowpy
Wyniki są publikowane w katalogu results/, a końcowe wyjście pipeline'u (przy uruchomieniu z domyślnymi parametrami) to zwykły plik tekstowy zawierający grafikę ASCII postaci wypowiadającej pozdrowienia zapisane wielkimi literami.
W tym zadaniu dodatkowym używamy pośredniej formy workflow'u Hello, która zawiera tylko dwa pierwsze procesy.
Podzbiór, z którym będziemy pracować, składa się z dwóch procesów: sayHello i convertToUpper.
Pełny kod workflow'u możesz zobaczyć poniżej.
Kod workflow'u
/*
* Parametry pipeline'u
*/
params.input_file = "greetings.csv"
/*
* Użyj echo, żeby wypisać 'Hello World!' na standardowe wyjście
*/
process sayHello {
publishDir 'results', mode: 'copy'
input:
val greeting
output:
path "${greeting}-output.txt"
script:
"""
echo '$greeting' > '$greeting-output.txt'
"""
}
/*
* Użyj narzędzia do zamiany tekstu, żeby przekonwertować pozdrowienie na wielkie litery
*/
process convertToUpper {
publishDir 'results', mode: 'copy'
input:
path input_file
output:
path "UPPER-${input_file}"
script:
"""
cat '$input_file' | tr '[a-z]' '[A-Z]' > UPPER-${input_file}
"""
}
workflow {
// utwórz kanał dla danych wejściowych z pliku CSV
greeting_ch = channel.fromPath(params.input_file).splitCsv().flatten()
// wyemituj pozdrowienie
sayHello(greeting_ch)
// przekonwertuj pozdrowienie na wielkie litery
convertToUpper(sayHello.out)
}
Uruchom workflow¶
Uruchommy workflow, żeby upewnić się, że działa zgodnie z oczekiwaniami.
N E X T F L O W ~ version 24.10.2
Launching `main.nf` [soggy_linnaeus] DSL2 - revision: bbf79d5c31
executor > local (6)
[f7/c3be66] sayHello (3) | 3 of 3 ✔
[cd/e15303] convertToUpper (3) | 3 of 3 ✔
GRATULACJE! Właśnie uruchomiłeś test!
„Chwila, co? Po prostu uruchomiłem workflow i zadziałał! Jak to jest test?"
Dobre pytanie!
Przeanalizujmy, co właśnie się stało.
Uruchomiłeś workflow z domyślnymi parametrami, potwierdziłeś, że działa, i jesteś zadowolony z wyników. To jest właśnie istota testowania. Jeśli przechodziłeś kurs Hello Nextflow, zauważyłeś zapewne, że zawsze zaczynaliśmy każdą sekcję od uruchomienia workflow'u, którego używaliśmy jako punktu startowego — żeby potwierdzić, że wszystko jest poprawnie skonfigurowane.
Testowanie oprogramowania zasadniczo wykonuje ten proces za nas.
Zapoznaj się z zadaniem¶
Twoim wyzwaniem jest dodanie standaryzowanych testów do tego workflow'u przy użyciu nf-test, aby łatwo było weryfikować, czy każda część nadal działa zgodnie z oczekiwaniami w przypadku wprowadzenia dalszych zmian.
Lista kontrolna gotowości¶
Myślisz, że jesteś gotowy, żeby zacząć?
- Rozumiem cel tego kursu i jego wymagania wstępne
- Moje środowisko jest uruchomione i działa
- Ustawiłem odpowiednio swój katalog roboczy
- Uruchomiłem workflow pomyślnie
- Rozumiem zadanie
Jeśli możesz zaznaczyć wszystkie pola, możesz zaczynać.
1. Inicjalizacja nf-test¶
Pakiet nf-test udostępnia polecenie inicjalizacji, które konfiguruje kilka rzeczy potrzebnych do rozpoczęcia tworzenia testów dla naszego projektu.
Powinno to dać następujące wyjście:
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Project configured. Configuration is stored in nf-test.config
Polecenie tworzy też katalog tests zawierający szkielet pliku konfiguracyjnego.
1.1. Generowanie testu nf-test¶
nf-test zawiera zestaw narzędzi do budowania plików nf-test, oszczędzając nam większości pracy. Dostępne są one jako podpolecenie generate. Wygenerujmy test dla pipeline'u:
> nf-test generate pipeline main.nf
Load source file '/workspaces/training/side-quests/nf-test/main.nf'
Wrote pipeline test file '/workspaces/training/side-quests/nf-test/tests/main.nf.test
SUCCESS: Generated 1 test files.
Spowoduje to utworzenie pliku main.nf.test w katalogu tests. To jest nasz plik testowy na poziomie pipeline'u. Po uruchomieniu tree tests/ powinieneś zobaczyć coś takiego:
Plik main.nf.test to nasz plik testowy na poziomie pipeline'u. Otwórzmy go i przyjrzyjmy się jego zawartości.
nextflow_pipeline {
name "Test Workflow main.nf"
script "main.nf"
test("Should run without failures") {
when {
params {
// define parameters here. Example:
// outdir = "tests/results"
}
}
then {
assert workflow.success
}
}
}
Poświęćmy chwilę na zrozumienie struktury pliku testowego.
Blok nextflow_pipeline jest punktem wejścia dla wszystkich testów na poziomie pipeline'u. Zawiera:
name: Nazwa testu.script: Ścieżka do skryptu pipeline'u.
Blok test to właściwy test. Zawiera:
when: Warunki, w których test powinien być uruchomiony. Obejmuje parametry, które zostaną użyte do uruchomienia pipeline'u.then: Asercje, które powinny zostać sprawdzone. Zawiera oczekiwane wyniki działania pipeline'u.
Mówiąc po ludzku, logika testu brzmi następująco: „Gdy do tego pipeline'u zostaną przekazane te parametry, wtedy oczekujemy tych wyników."
To nie jest jeszcze funkcjonalny test — w następnej sekcji pokażemy, jak go takim uczynić.
Uwaga o nazwach testów¶
W powyższym przykładzie użyliśmy domyślnej nazwy „Should run without failures", która jest odpowiednia dla podstawowego testu sprawdzającego jedynie, czy pipeline uruchamia się pomyślnie. Jednak w miarę dodawania bardziej szczegółowych przypadków testowych powinniśmy używać bardziej opisowych nazw, wskazujących, co faktycznie testujemy. Na przykład:
- „Should convert input to uppercase" — przy testowaniu konkretnej funkcjonalności
- „Should handle empty input gracefully" — przy testowaniu przypadków brzegowych
- „Should respect max memory parameter" — przy testowaniu ograniczeń zasobów
- „Should create expected output files" — przy testowaniu generowania plików
Dobre nazwy testów powinny:
- Zaczynać się od „Should", żeby jasno określić oczekiwane zachowanie
- Opisywać konkretną funkcjonalność lub scenariusz, który jest testowany
- Być na tyle jasne, że gdy test nie przejdzie, od razu wiadomo, jaka funkcjonalność jest zepsuta
W miarę dodawania kolejnych asercji i szczegółowych przypadków testowych będziemy używać tych bardziej opisowych nazw, żeby jasno określić, co każdy test weryfikuje.
1.2. Uruchomienie testu¶
Uruchommy test, żeby zobaczyć, co się stanie.
> nf-test test tests/main.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Workflow main.nf
Test [693ba951] 'Should run without failures' FAILED (4.652s)
Assertion failed:
assert workflow.success
| |
workflow false
Nextflow stdout:
ERROR ~ No such file or directory: /workspaces/training/side-quests/nf-test/.nf-test/tests/693ba951a20fec36a5a9292ed1cc8a9f/greetings.csv
-- Check '/workspaces/training/side-quests/nf-test/.nf-test/tests/693ba951a20fec36a5a9292ed1cc8a9f/meta/nextflow.log' file for details
Nextflow stderr:
FAILURE: Executed 1 tests in 4.679s (1 failed)
Test nie przeszedł! Co się stało?
- nf-test próbował uruchomić pipeline w obecnym stanie, używając ustawień z bloku
when:
when {
params {
// define parameters here. Example:
// outdir = "tests/results"
}
}
- nf-test sprawdził status pipeline'u i porównał go z blokiem
when:
Zwróć uwagę, jak nf-test poinformował o niepowodzeniu pipeline'u i podał komunikat błędu z Nextflow:
ERROR ~ No such file or directory: /workspaces/training/side-quests/nf-test/.nf-test/tests/693ba951a20fec36a5a9292ed1cc8a9f/greetings.csv
Na czym polegał problem? Pamiętaj, że pipeline ma plik greetings.csv w katalogu projektu. Gdy nf-test uruchamia pipeline, szuka tego pliku, ale nie może go znaleźć. Plik jest tam, więc co się dzieje? Jeśli spojrzymy na ścieżkę, zobaczymy, że test odbywa się w ścieżce ./nf-test/tests/longHashString/. Podobnie jak Nextflow, nf-test tworzy nowy katalog dla każdego testu, żeby wszystko było izolowane. Plik z danymi nie znajduje się tam, więc musimy poprawić ścieżkę do pliku w oryginalnym teście.
Wróćmy do pliku testowego i zmieńmy ścieżkę do pliku w bloku when.
Możesz się zastanawiać, jak wskazać korzeń pipeline'u w teście. Ponieważ jest to częsta sytuacja, nf-test udostępnia szereg zmiennych globalnych, które ułatwiają nam życie. Pełną listę znajdziesz tutaj, ale na razie użyjemy zmiennej projectDir, która oznacza korzeń projektu pipeline'u.
Uruchommy test ponownie, żeby sprawdzić, czy działa.
> nf-test test tests/main.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Workflow main.nf
Test [1d4aaf12] 'Should run without failures' PASSED (1.619s)
SUCCESS: Executed 1 tests in 1.626s
Sukces! Pipeline uruchamia się pomyślnie i test przechodzi. Uruchamiaj go tyle razy, ile chcesz — zawsze otrzymasz ten sam wynik!
Domyślnie wyjście Nextflow jest ukryte, ale żeby przekonać się, że nf-test rzeczywiście uruchamia workflow, możesz użyć flagi --verbose:
> nf-test test tests/main.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Workflow main.nf
Test [693ba951] 'Should run without failures'
> Nextflow 24.10.4 is available - Please consider updating your version to it
> N E X T F L O W ~ version 24.10.0
> Launching `/workspaces/training/side-quests/nf-test/main.nf` [zen_ampere] DSL2 - revision: bbf79d5c31
> [2b/61e453] Submitted process > sayHello (2)
> [31/4e1606] Submitted process > sayHello (1)
> [bb/5209ee] Submitted process > sayHello (3)
> [83/83db6f] Submitted process > convertToUpper (2)
> [9b/3428b1] Submitted process > convertToUpper (1)
> [ca/0ba51b] Submitted process > convertToUpper (3)
PASSED (5.206s)
SUCCESS: Executed 1 tests in 5.239s
1.3. Dodawanie asercji¶
Prostym sprawdzeniem jest upewnienie się, że nasz pipeline uruchamia wszystkie oczekiwane procesy i żadnego nie pomija po cichu. Pamiętaj, że nasz pipeline uruchamia 6 procesów — jeden sayHello i jeden convertToUpper dla każdego z 3 pozdrowień.
Dodajmy asercję do naszego testu, żeby sprawdzić, czy pipeline uruchamia oczekiwaną liczbę procesów. Zaktualizujemy też nazwę testu, żeby lepiej odzwierciedlała to, co testujemy.
Nazwa testu teraz lepiej odzwierciedla to, co faktycznie weryfikujemy — nie tylko to, że pipeline uruchamia się bez błędów, ale że uruchamia oczekiwaną liczbę procesów.
Uruchommy test ponownie, żeby sprawdzić, czy działa.
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Workflow main.nf
Test [1d4aaf12] 'Should run successfully with correct number of processes' PASSED (1.567s)
SUCCESS: Executed 1 tests in 1.588s
Sukces! Pipeline uruchamia się pomyślnie i test przechodzi. Zaczęliśmy teraz testować szczegóły pipeline'u, a nie tylko jego ogólny status.
1.4. Testowanie wyjścia¶
Dodajmy asercję do naszego testu, żeby sprawdzić, czy plik wyjściowy został utworzony. Dodamy go jako osobny test z informacyjną nazwą, żeby wyniki były łatwiejsze do interpretacji.
Uruchom test ponownie, żeby sprawdzić, czy działa.
> nf-test test tests/main.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Workflow main.nf
Test [f0e08a68] 'Should run successfully with correct number of processes' PASSED (8.144s)
Test [d7e32a32] 'Should produce correct output files' PASSED (6.994s)
SUCCESS: Executed 2 tests in 15.165s
Sukces! Testy przechodzą, ponieważ pipeline zakończył się pomyślnie, uruchomiono właściwą liczbę procesów i pliki wyjściowe zostały utworzone. Powinieneś też teraz zobaczyć, jak przydatne jest nadawanie testom informacyjnych nazw.
To tylko wierzchołek góry lodowej — możemy pisać kolejne asercje sprawdzające szczegóły pipeline'u, ale na razie przejdźmy do testowania jego wewnętrznych komponentów.
Podsumowanie¶
Wiesz już, jak pisać testy nf-test dla pipeline'u.
Co dalej?¶
Naucz się testować proces Nextflow.
2. Testowanie procesu Nextflow¶
Nie musimy pisać testów dla każdej części pipeline'u, ale im więcej testów mamy, tym bardziej kompleksowe jest nasze pokrycie i tym większą pewność możemy mieć, że pipeline działa zgodnie z oczekiwaniami. W tej sekcji przetestujemy oba procesy pipeline'u jako indywidualne jednostki.
2.1. Testowanie procesu sayHello¶
Zacznijmy od procesu sayHello.
Użyjmy ponownie polecenia nf-test generate, żeby wygenerować testy dla procesu.
> nf-test generate process main.nf
Load source file '/workspaces/training/side-quests/nf-test/main.nf'
Wrote process test file '/workspaces/training/side-quests/nf-test/tests/main.sayhello.nf.test
Wrote process test file '/workspaces/training/side-quests/nf-test/tests/main.converttoupper.nf.test
SUCCESS: Generated 2 test files.
Skupmy się teraz na procesie sayhello w pliku main.sayhello.nf.test.
Otwórzmy plik i przyjrzyjmy się jego zawartości.
nextflow_process {
name "Test Process sayHello"
script "main.nf"
process "sayHello"
test("Should run without failures") {
when {
params {
// define parameters here. Example:
// outdir = "tests/results"
}
process {
"""
// define inputs of the process here. Example:
// input[0] = file("test-file.txt")
"""
}
}
then {
assert process.success
assert snapshot(process.out).match()
}
}
}
Podobnie jak wcześniej, zaczynamy od szczegółów testu, po których następują bloki when i then. Mamy jednak dodatkowy blok process, który pozwala nam zdefiniować dane wejściowe do procesu.
Uruchommy test, żeby sprawdzić, czy działa.
> nf-test test tests/main.sayhello.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process sayHello
Test [1eaad118] 'Should run without failures' FAILED (4.876s)
Assertion failed:
assert process.success
| |
| false
sayHello
Nextflow stdout:
Process `sayHello` declares 1 input but was called with 0 arguments
Nextflow stderr:
FAILURE: Executed 1 tests in 4.884s (1 failed)
Test nie przechodzi, ponieważ proces sayHello deklaruje 1 wejście, ale został wywołany z 0 argumentami. Naprawmy to, dodając wejście do procesu. Pamiętaj z Hello Workflow (i sekcji rozgrzewki powyżej), że nasz proces sayHello przyjmuje pojedyncze wejście wartości, które musimy dostarczyć. Powinniśmy też poprawić nazwę testu, żeby lepiej odzwierciedlała to, co testujemy.
| tests/main.sayhello.nf.test | |
|---|---|
| tests/main.sayhello.nf.test | |
|---|---|
Uruchommy test ponownie, żeby sprawdzić, czy działa.
> nf-test test tests/main.sayhello.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process sayHello
Test [f91a1bcd] 'Should run without failures and produce correct output' PASSED (1.604s)
Snapshots:
1 created [Should run without failures and produce correct output]
Snapshot Summary:
1 created
SUCCESS: Executed 1 tests in 1.611s
Sukces! Test przechodzi, ponieważ proces sayHello uruchomił się pomyślnie i wyjście zostało utworzone.
2.2. Sprawdzenie snapshotu utworzonego przez test¶
Jeśli spojrzymy na plik tests/main.sayhello.nf.test, zobaczymy, że używa metody snapshot() w bloku asercji:
Mówi to nf-test, żeby utworzył snapshot wyjścia procesu sayHello. Przyjrzyjmy się zawartości pliku snapshotu.
Nie będziemy go tu drukować, ale powinieneś zobaczyć plik JSON zawierający szczegóły procesu i jego wyjść. W szczególności możemy zobaczyć linię wyglądającą tak:
Reprezentuje to wyjścia utworzone przez proces sayHello, które testujemy wprost. Jeśli ponownie uruchomimy test, program sprawdzi, czy nowe wyjście pasuje do wyjścia pierwotnie zarejestrowanego. To szybki, prosty sposób testowania, czy wyjścia procesów się nie zmieniają — dlatego nf-test udostępnia go jako domyślny.
Ostrzeżenie
Oznacza to, że musimy być pewni, że wyjście zarejestrowane w pierwotnym uruchomieniu jest poprawne!
Jeśli w toku dalszego rozwoju coś w kodzie zmieni się tak, że wyjście będzie inne, test nie przejdzie i będziemy musieli ustalić, czy zmiana jest oczekiwana, czy nie.
- Jeśli okaże się, że coś w kodzie się zepsuło, będziemy musieli to naprawić, oczekując, że poprawiony kod przejdzie test.
- Jeśli jest to oczekiwana zmiana (np. narzędzie zostało ulepszone i wyniki są lepsze), będziemy musieli zaktualizować snapshot, żeby zaakceptować nowe wyjście jako referencję do porównania. nf-test ma do tego celu parametr
--update-snapshot.
Możemy uruchomić test ponownie i zobaczyć, że powinien przejść:
> nf-test test tests/main.sayhello.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process sayHello
Test [f91a1bcd] 'Should run without failures and produce correct output' PASSED (1.675s)
SUCCESS: Executed 1 tests in 1.685s
Sukces! Test przechodzi, ponieważ proces sayHello uruchomił się pomyślnie i wyjście pasuje do snapshotu.
2.3. Alternatywa dla snapshotów: bezpośrednie asercje treści¶
Snapshoty świetnie nadają się do wykrywania wszelkich zmian w wyjściu, ale czasem chcemy zweryfikować konkretną treść bez tak rygorystycznego wymagania, żeby cały plik pasował. Na przykład:
- Gdy części wyjścia mogą się zmieniać (znaczniki czasu, losowe identyfikatory itp.), ale pewna kluczowa treść musi być obecna
- Gdy chcemy sprawdzić konkretne wzorce lub wartości w wyjściu
- Gdy chcemy uczynić test bardziej precyzyjnym co do tego, co stanowi sukces
Oto jak możemy zmodyfikować nasz test, żeby sprawdzał konkretną treść:
| tests/main.sayhello.nf.test | |
|---|---|
Zwróć uwagę, że nf-test widzi wyjścia procesu jako listę list, więc process.out[0][0] pobiera pierwszą część pierwszego elementu kanału (czyli pierwszej „emisji") z tego procesu.
To podejście:
- Jasno określa, czego oczekujemy w wyjściu
- Jest bardziej odporne na nieistotne zmiany w wyjściu
- Dostarcza lepszych komunikatów błędów, gdy testy nie przechodzą
- Umożliwia bardziej złożone walidacje (wzorce regex, porównania numeryczne itp.)
Uruchommy test, żeby sprawdzić, czy działa.
> nf-test test tests/main.sayhello.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process sayHello
Test [58df4e4b] 'Should run without failures and contain expected greeting' PASSED (7.196s)
SUCCESS: Executed 1 tests in 7.208s
2.4. Testowanie procesu convertToUpper¶
Otwórzmy plik tests/main.converttoupper.nf.test i przyjrzyjmy się jego zawartości:
nextflow_process {
name "Test Process convertToUpper"
script "main.nf"
process "convertToUpper"
test("Should run without failures") {
when {
params {
// define parameters here. Example:
// outdir = "tests/results"
}
process {
"""
// define inputs of the process here. Example:
// input[0] = file("test-file.txt")
"""
}
}
then {
assert process.success
assert snapshot(process.out).match()
}
}
}
To podobny test do tego dla procesu sayHello, ale testuje proces convertToUpper. Wiemy, że ten test nie przejdzie, ponieważ — podobnie jak w przypadku sayHello — proces convertToUpper przyjmuje pojedyncze wejście ścieżki, którego nie podaliśmy.
Musimy teraz dostarczyć pojedynczy plik wejściowy do procesu convertToUpper, zawierający tekst, który chcemy przekonwertować na wielkie litery. Możemy to zrobić na wiele sposobów:
- Możemy utworzyć dedykowany plik do testów
- Możemy ponownie użyć istniejącego pliku
data/greetings.csv - Możemy go utworzyć w locie w ramach testu
Na razie ponownie użyjmy istniejącego pliku data/greetings.csv, korzystając z przykładu użytego w teście na poziomie pipeline'u. Jak poprzednio, możemy nazwać test tak, żeby lepiej odzwierciedlał to, co testujemy, ale tym razem zostawmy „snapshot" treści zamiast sprawdzać konkretne ciągi znaków (jak zrobiliśmy to w przypadku drugiego procesu).
| tests/main.converttoupper.nf.test | |
|---|---|
| tests/main.converttoupper.nf.test | |
|---|---|
I uruchommy test!
> nf-test test tests/main.converttoupper.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process convertToUpper
Test [c59b6044] 'Should run without failures and produce correct output' PASSED (1.755s)
Snapshots:
1 created [Should run without failures and produce correct output]
Snapshot Summary:
1 created
SUCCESS: Executed 1 tests in 1.764s
Zwróć uwagę, że utworzyliśmy plik snapshotu dla procesu convertToUpper w tests/main.converttoupper.nf.test.snap. Jeśli uruchomimy test ponownie, powinniśmy zobaczyć, że nf-test znowu przechodzi.
> nf-test test tests/main.converttoupper.nf.test
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process convertToUpper
Test [c59b6044] 'Should run without failures and produce correct output' PASSED (1.798s)
SUCCESS: Executed 1 tests in 1.811s
Podsumowanie¶
Wiesz już, jak pisać testy dla procesu Nextflow i je uruchamiać.
Co dalej?¶
Naucz się uruchamiać wszystkie testy naraz!
3. Uruchamianie testów dla całego repozytorium¶
Uruchamianie nf-test dla każdego komponentu osobno jest możliwe, ale żmudne i podatne na błędy. Czy nie możemy przetestować wszystkiego naraz?
Możemy!
Uruchommy nf-test na całym repozytorium.
3.1. Uruchamianie nf-test na całym repozytorium¶
Możemy uruchomić nf-test na całym repozytorium, używając polecenia nf-test test.
Zwróć uwagę, że używamy po prostu ., żeby uruchomić wszystko z naszego bieżącego katalogu. Obejmie to każdy test!
> nf-test test .
🚀 nf-test 0.9.3
https://www.nf-test.com
(c) 2021 - 2024 Lukas Forer and Sebastian Schoenherr
Test Process convertToUpper
Test [3d26d9af] 'Should run without failures and produce correct output' PASSED (4.155s)
Test Workflow main.nf
Test [f183df37] 'Should run successfully with correct number of processes' PASSED (3.33s)
Test [d7e32a32] 'Should produce correct output files' PASSED (3.102s)
Test Process sayHello
Test [58df4e4b] 'Should run without failures and contain expected greeting' PASSED (2.614s)
SUCCESS: Executed 4 tests in 13.481s
Spójrz na to! Uruchomiliśmy 4 testy — 1 dla każdego procesu i 2 dla całego pipeline'u — jednym poleceniem. Wyobraź sobie, jak potężne jest to w przypadku dużej bazy kodu!
Podsumowanie¶
W tym zadaniu dodatkowym nauczyłeś się wykorzystywać funkcje nf-test do tworzenia i uruchamiania testów dla poszczególnych procesów, a także testów end-to-end dla całego pipeline'u. Znasz teraz dwa główne podejścia do walidacji wyjść — snapshoty i bezpośrednie asercje treści — oraz wiesz, kiedy używać każdego z nich. Wiesz też, jak uruchamiać testy jeden po drugim lub dla całego projektu.
Stosowanie tych technik we własnej pracy pozwoli Ci zapewnić, że:
- Twój kod działa zgodnie z oczekiwaniami
- Zmiany nie psują istniejącej funkcjonalności
- Inni programiści mogą wnosić wkład z pewnością siebie
- Problemy można szybko zidentyfikować i naprawić
- Treść wyjść spełnia oczekiwania
Kluczowe wzorce¶
- Testy na poziomie pipeline'u:
- Podstawowe testowanie sukcesu
- Weryfikacja liczby procesów
- Sprawdzanie istnienia plików wyjściowych
- Testy na poziomie procesu
- Dwa podejścia do walidacji wyjść:
- Używanie snapshotów do pełnej weryfikacji wyjścia
- Używanie bezpośrednich asercji treści do sprawdzania konkretnej zawartości
- Uruchamianie wszystkich testów w repozytorium jednym poleceniem
Dodatkowe zasoby¶
Zajrzyj do dokumentacji nf-test, żeby poznać bardziej zaawansowane funkcje testowania i najlepsze praktyki. Możesz chcieć:
- Dodać bardziej kompleksowe asercje do swoich testów
- Pisać testy dla przypadków brzegowych i warunków błędów
- Skonfigurować ciągłą integrację, żeby testy uruchamiały się automatycznie
- Poznać inne rodzaje testów, takie jak testy workflow'u i modułów
- Zgłębić bardziej zaawansowane techniki walidacji treści
Pamiętaj: Testy to żywa dokumentacja tego, jak Twój kod powinien się zachowywać. Im więcej testów piszesz i im bardziej szczegółowe są Twoje asercje, tym większą pewność możesz mieć co do niezawodności swojego pipeline'u.
Co dalej?¶
Wróć do menu zadań dodatkowych lub kliknij przycisk w prawym dolnym rogu strony, żeby przejść do następnego tematu na liście.