Oto zalecane praktyki testowania kodu w repozytorium TensorFlow .
Zanim zaczniesz
Przed przesłaniem kodu źródłowego do projektu TensorFlow przejrzyj plik CONTRIBUTING.md
w repozytorium GitHub projektu. (Na przykład zobacz plik CONTRIBUTING.md dla podstawowego repozytorium TensorFlow ). Wszyscy współtwórcy kodu są zobowiązani do podpisania umowy licencyjnej współautora (CLA).
Ogólne zasady
Zależy tylko od tego, czego używasz w swoich regułach BUILD
TensorFlow to duża biblioteka, a pisanie testu jednostkowego dla jego podmodułów polegało na zależności od pełnego pakietu. Jednak bazel
to wyłączenie analizy opartej na zależnościach bazel
. Oznacza to, że systemy ciągłej integracji nie mogą w inteligentny sposób eliminować niepowiązanych testów dla przebiegów przed przesłaniem / po przesłaniu. Jeśli polegasz tylko na modułach podrzędnych, które testujesz w pliku BUILD
, zaoszczędzisz czas wszystkich programistów TensorFlow i dużo cennej mocy obliczeniowej.
Jednak modyfikowanie zależności kompilacji w celu pominięcia pełnych elementów docelowych TF powoduje pewne ograniczenia dotyczące tego, co można zaimportować w kodzie Pythona. Nie będzie już można używać instrukcji import tensorflow as tf
instrukcji import tensorflow as tf
w testach jednostkowych. Jest to jednak opłacalny kompromis, ponieważ oszczędza wszystkim programistom wykonywania tysięcy niepotrzebnych testów.
Cały kod powinien mieć testy jednostkowe
Dla każdego napisanego kodu należy również napisać jego testy jednostkowe. Jeśli napiszesz nowy plik foo.py
, powinieneś umieścić jego testy jednostkowe w foo_test.py
i przesłać go w ramach tej samej zmiany. Dąż do> 90% przyrostowego pokrycia testami dla całego kodu.
Unikaj używania natywnych reguł testu bazel w TF
TF ma wiele subtelności podczas przeprowadzania testów. Pracowaliśmy, aby ukryć wszystkie te zawiłości w naszych makrach bazela. Aby uniknąć konieczności radzenia sobie z nimi, użyj następujących zamiast natywnych reguł testowych. Zauważ, że wszystkie z nich są zdefiniowane w tensorflow/tensorflow.bzl
W przypadku testów CC użyj tf_cc_test
, tf_gpu_cc_test
, tf_gpu_only_cc_test
. W przypadku testów Pythona użyj tf_py_test
lub gpu_py_test
. Jeśli potrzebujesz czegoś bardzo zbliżonego do natywnej reguły py_test
, użyj zamiast tego reguły zdefiniowanej w tensorflow.bzl. Wystarczy dodać następujący wiersz na początku pliku BUILD: load(“tensorflow/tensorflow.bzl”, “py_test”)
Miej świadomość, gdzie wykonywany jest test
Kiedy piszesz test, nasza infra testowa może zająć się przeprowadzaniem testów na CPU, GPU i akceleratorach, jeśli napiszesz je odpowiednio. Mamy zautomatyzowane testy, które działają w systemach Linux, macOS, Windows, które mają systemy z procesorami graficznymi lub bez nich. Wystarczy wybrać jedno z wymienionych powyżej makr, a następnie użyć tagów, aby ograniczyć miejsce ich wykonywania.
tag
manual
wykluczy Twój test z dowolnego miejsca. Obejmuje to ręczne wykonywanie testów, które używają wzorców, takich jakbazel test tensorflow/…
no_oss
wykluczy twój test z uruchamiania w oficjalnej infrastrukturze testowej TF OSS.no_mac
lubno_windows
mogą służyć do wykluczania testu z odpowiednich zestawów testów systemu operacyjnego.Znacznika
no_gpu
można użyć, aby wykluczyć test z uruchamiania w pakietach testów GPU.
Sprawdź, czy testy są uruchamiane w oczekiwanych zestawach testów
TF ma sporo zestawów testowych. Czasami ich konfiguracja może być myląca. Mogą występować różne problemy, które powodują pomijanie testów w ciągłych kompilacjach. Dlatego należy sprawdzić, czy testy działają zgodnie z oczekiwaniami. Aby to zrobić:
- Poczekaj, aż wstępne przesłania żądania ściągnięcia (PR) przebiegną do końca.
- Przewiń w dół swojego PR, aby zobaczyć kontrole stanu.
- Kliknij link „Szczegóły” po prawej stronie dowolnego czeku Kokoro.
- Sprawdź listę „Cele”, aby znaleźć nowo dodane cele.
Każda klasa / jednostka powinna mieć swój własny plik testu jednostkowego
Oddzielne klasy testowe pomagają nam lepiej izolować awarie i zasoby. Prowadzą do znacznie krótszych i łatwiejszych do odczytania plików testowych. Dlatego wszystkie twoje pliki Pythona powinny mieć co najmniej jeden odpowiedni plik testowy (dla każdego foo.py
powinien mieć foo_test.py
). W przypadku bardziej skomplikowanych testów, takich jak testy integracyjne, które wymagają różnych konfiguracji, można dodać więcej plików testowych.
Prędkość i czasy pracy
Odłamki powinny być używane w jak najmniejszym stopniu
Zamiast dzielenia na fragmenty rozważ:
- Zmniejszanie rozmiarów testów
- Jeśli powyższe nie jest możliwe, podziel testy
Sharding pomaga zmniejszyć ogólne opóźnienie testu, ale to samo można osiągnąć, dzieląc testy na mniejsze cele. Dzielenie testów daje nam lepszy poziom kontroli nad każdym testem, minimalizując niepotrzebne wstępne uruchomienia i zmniejszając utratę pokrycia z powodu wyłączenia całego obiektu docelowego z powodu nieprawidłowo działającego przypadku testowego. Ponadto dzielenie na fragmenty wiąże się z ukrytymi kosztami, które nie są tak oczywiste, takie jak uruchomienie całego kodu inicjującego test dla wszystkich fragmentów. Ten problem został zgłoszony do nas przez zespoły infra jako źródło, które powoduje dodatkowe obciążenie.
Mniejsze testy są lepsze
Im szybciej przeprowadzisz testy, tym większe prawdopodobieństwo, że ludzie je wykonają. Jedna dodatkowa sekunda na test może skumulować się do godzin dodatkowego czasu spędzonego na przeprowadzaniu testu przez programistów i naszą infrastrukturę. Postaraj się, aby testy działały krócej niż 30 sekund (w trybie non-opt!) I spraw, by były małe. Oznaczaj testy jako średnie tylko w ostateczności. Infra nie przeprowadza żadnych dużych testów jako przesłanych wstępnych lub po przesłaniu! Dlatego napisz duży test tylko wtedy, gdy masz zamiar ustalić, gdzie będzie on wykonywany. Kilka wskazówek, jak przyspieszyć działanie testów:
- Wykonuj mniej powtórzeń treningu w teście
- Rozważ użycie wstrzykiwania zależności w celu zastąpienia ciężkich zależności testowanego systemu prostymi podróbkami.
- Rozważ użycie mniejszych danych wejściowych w testach jednostkowych
- Jeśli nic innego nie działa, spróbuj podzielić plik testowy.
Czasy testu powinny mieć na celu połowę limitu czasu testu, aby uniknąć płatków
W bazel
celów testowych bazel
, małe testy mają 1-minutowy bazel
czasu. Średnie limity czasu testu to 5 minut. Duże testy po prostu nie są wykonywane przez test TensorFlow infra. Jednak wiele testów nie jest deterministycznych pod względem czasu, jaki zajmują. Z różnych powodów twoje testy mogą od czasu do czasu zająć więcej czasu. A jeśli oznaczysz test, który trwa średnio 50 sekund jako mały, test zakończy się niepowodzeniem, jeśli zostanie zaplanowany na komputerze ze starym procesorem. Dlatego w przypadku małych testów należy dążyć do 30 sekundowego średniego czasu działania. Celuj przez 2 minuty i 30 sekund średniego czasu działania dla średnich testów.
Zmniejsz liczbę próbek i zwiększ tolerancje podczas treningu
Powolne testy odstraszają współpracowników. Treningi biegowe w testach mogą być bardzo powolne. Preferuj wyższe tolerancje, aby móc używać mniej próbek w testach i utrzymywać je wystarczająco szybko (maksymalnie 2,5 minuty).
Wyeliminuj niedeterminizm i łuski
Pisz testy deterministyczne
Testy jednostkowe powinny zawsze być deterministyczne. Wszystkie testy działające na TAP i gitarze powinny za każdym razem przebiegać w ten sam sposób, jeśli nie ma na nie wpływu żadna zmiana kodu. Aby to zapewnić, poniżej przedstawiono kilka punktów, które należy wziąć pod uwagę.
Zawsze zasiewaj każde źródło stochastyczności
Dowolny generator liczb losowych lub inne źródła stochastyczności mogą powodować łuszczenie się. Dlatego każdy z nich musi zostać zaszczepiony. Oprócz tego, że testy są mniej łuszczące, zapewnia to powtarzalność wszystkich testów. Różne sposoby ustawienia nasion, które mogą być potrzebne w testach TF, to:
# Python RNG
import random
random.seed(42)
# Numpy RNG
import numpy as np
np.random.seed(42)
# TF RNG
from tensorflow.python.framework import random_seed
random_seed.set_seed(42)
Unikaj używania sleep
w testach wielowątkowych
Korzystanie z funkcji sleep
w testach może być główną przyczyną łuszczenia się. Zwłaszcza w przypadku korzystania z wielu wątków użycie trybu uśpienia do oczekiwania na inny wątek nigdy nie będzie deterministyczne. Jest to spowodowane tym, że system nie jest w stanie zagwarantować żadnej kolejności wykonywania różnych wątków lub procesów. Dlatego preferuj deterministyczne konstrukcje synchronizacji, takie jak muteksy.
Sprawdź, czy test jest niestabilny
Płatki powodują, że narzędzia budowlane i programiści tracą wiele godzin. Są trudne do wykrycia i trudne do debugowania. Mimo że istnieją zautomatyzowane systemy wykrywające łuszczenie, muszą one zebrać setki testów, zanim będą mogły dokładnie przeprowadzić testy denylistyczne. Nawet jeśli wykryją, denylizują twoje testy i tracą zasięg testów. Dlatego autorzy testów powinni podczas pisania testów sprawdzić, czy ich testy są niestabilne. Można to łatwo zrobić, uruchamiając test z flagą: --runs_per_test=1000
Użyj TensorFlowTestCase
TensorFlowTestCase podejmuje niezbędne środki ostrożności, takie jak zaszczepianie wszystkich generatorów liczb losowych używanych w celu jak największego zmniejszenia łuszczenia się. Gdy odkryjemy i naprawimy więcej źródeł niestabilności, wszystkie one zostaną dodane do TensorFlowTestCase. Dlatego podczas pisania testów dla tensorflow należy używać TensorFlowTestCase. TensorFlowTestCase jest zdefiniowany tutaj: tensorflow/python/framework/test_util.py
Pisz testy hermetyczne
Testy hermetyczne nie wymagają żadnych zewnętrznych zasobów. Są spakowani we wszystko, czego potrzebują, i po prostu rozpoczynają fałszywe usługi, których mogą potrzebować. Wszelkie usługi inne niż Twoje testy są źródłem niedeterminizmu. Nawet przy 99% dostępności innych usług, sieć może się załamać, odpowiedź RPC może być opóźniona i możesz skończyć z niewytłumaczalnym komunikatem o błędzie. Usługami zewnętrznymi mogą być między innymi GCS, S3 lub dowolna witryna internetowa.