Oto zalecane praktyki testowania kodu w repozytorium TensorFlow .
Zanim zaczniesz
Zanim przekażesz kod źródłowy do projektu TensorFlow, przejrzyj plik CONTRIBUTING.md
w repozytorium GitHub projektu. (Na przykład zobacz plik CONTRIBUTING.md dla podstawowego repozytorium TensorFlow .) Wszyscy współautorzy kodu muszą podpisać umowę licencyjną współautora (CLA).
Ogólne zasady
Zależy tylko od tego, czego użyjesz w regułach BUILD
TensorFlow jest dużą biblioteką i w zależności od pełnego pakietu pisanie testu jednostkowego dla jego podmodułów było powszechną praktyką. Jednakże powoduje to wyłączenie analizy opartej na zależnościach bazel
. Oznacza to, że systemy ciągłej integracji nie są w stanie w inteligentny sposób eliminować niepowiązanych testów przed i po przesłaniu. Jeśli polegasz tylko na submodułach, które testujesz w swoim pliku BUILD
, zaoszczędzisz czas wszystkim programistom TensorFlow i mnóstwo cennej mocy obliczeniowej.
Jednak modyfikacja zależności kompilacji w celu pominięcia pełnych celów TF powoduje pewne ograniczenia dotyczące tego, co można zaimportować do kodu Pythona. Nie będziesz już mógł używać import tensorflow as tf
w testach jednostkowych. Jest to jednak opłacalny kompromis, ponieważ oszczędza wszystkim programistom konieczności przeprowadzania tysięcy niepotrzebnych testów.
Cały kod powinien mieć testy jednostkowe
Dla każdego napisanego kodu powinieneś także napisać jego testy jednostkowe. Jeśli napiszesz nowy plik foo.py
, powinieneś umieścić jego testy jednostkowe w foo_test.py
i przesłać je w ramach tej samej zmiany. Dąż do uzyskania >90% przyrostowego pokrycia całego kodu testami.
Unikaj używania natywnych reguł testowych bazela w TF
TF ma wiele subtelności podczas uruchamiania testów. Pracowaliśmy nad ukryciem wszystkich tych zawiłości w naszych makrach bazelowych. Aby uniknąć konieczności radzenia sobie z nimi, użyj poniższych zamiast natywnych reguł testowych. Należy pamiętać, że wszystkie 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 górze pliku BUILD: load(“tensorflow/tensorflow.bzl”, “py_test”)
Bądź świadomy, gdzie wykonywany jest test
Kiedy piszesz test, nasza infrastruktura testowa może zająć się wykonaniem testów na procesorze, karcie graficznej i akceleratorach, jeśli odpowiednio je napiszesz. Posiadamy zautomatyzowane testy działające na systemach Linux, macos i Windows, które mają systemy z procesorami graficznymi lub bez. Wystarczy wybrać jedno z makr wymienionych powyżej, a następnie użyć tagów, aby ograniczyć miejsce ich wykonywania.
tag
manual
wykluczy uruchomienie testu w dowolnym miejscu. Obejmuje to ręczne wykonanie testów wykorzystujących wzorce, takie jakbazel test tensorflow/…
no_oss
wykluczy działanie Twojego testu w oficjalnej infrastrukturze testowej TF OSS.Tagi
no_mac
lubno_windows
można wykorzystać do wykluczenia testu z odpowiednich zestawów testów systemu operacyjnego.Tagu
no_gpu
można użyć, aby wykluczyć uruchamianie testu w zestawach testów GPU.
Sprawdź, czy testy działają 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ą pominięcie testów w ciągłych kompilacjach. Dlatego powinieneś sprawdzić, czy testy działają zgodnie z oczekiwaniami. Aby to zrobić:
- Poczekaj, aż wstępne przesłania w ramach żądania ściągnięcia (PR) zostaną zakończone.
- Przewiń w dół swojego PR, aby zobaczyć kontrole statusu.
- 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 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 ustawień, dobrze jest dodać więcej plików testowych.
Prędkość i czasy działania
Shardingu należy używać jak najrzadziej
Zamiast shardingu, rozważ:
- Zmniejszanie rozmiaru testów
- Jeżeli powyższe nie jest możliwe, podziel testy na części
Fragmentowanie pomaga zmniejszyć ogólne opóźnienie testu, ale to samo można osiągnąć, dzieląc testy na mniejsze cele. Dzielenie testów zapewnia nam lepszą kontrolę nad każdym testem, minimalizując niepotrzebne przebiegi przed przesłaniem i zmniejszając utratę zasięgu spowodowaną przez policjanta budowlanego, który unieruchomił cały cel z powodu nieprawidłowego działania przypadku testowego. Co więcej, fragmentowanie wiąże się z ukrytymi kosztami, które nie są tak oczywiste, jak uruchomienie całego kodu inicjującego test dla wszystkich fragmentów. Zespoły infrastruktury przekazały nam ten problem jako źródło powodujące dodatkowe obciążenie.
Lepsze są mniejsze testy
Im szybciej będą przeprowadzane testy, tym większe prawdopodobieństwo, że ludzie je przeprowadzą. Jedna dodatkowa sekunda na test może się skumulować w godzinach dodatkowego czasu spędzonego na przeprowadzaniu testu przez programistów i naszą infrastrukturę. Staraj się, aby testy trwały krócej niż 30 sekund (w trybie nieopt!) i zmniejsz je. Oznacz swoje testy jako średnie tylko w ostateczności. Infra nie przeprowadza żadnych dużych testów przed lub po przesłaniu! Dlatego duży test pisz tylko wtedy, gdy masz zamiar ustalić miejsce jego uruchomienia. Kilka wskazówek, dzięki którym testy będą działać szybciej:
- Wykonuj mniej iteracji treningu w swoim teście
- Rozważ użycie wstrzykiwania zależności, aby zastąpić duże 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 testów powinny obejmować połowę limitu czasu rozmiaru testu, aby uniknąć płatków
W przypadku celów testu bazel
małe testy mają 1-minutowe limity czasu. Średnie limity czasu testu wynoszą 5 minut. Duże testy po prostu nie są wykonywane przez infrastrukturę testową TensorFlow. Jednak wiele testów nie jest deterministycznych pod względem czasu ich trwania. Z różnych powodów Twoje testy mogą od czasu do czasu zająć więcej czasu. A jeśli oznaczysz jako mały test trwający średnio 50 sekund, test będzie się błędnie układał, jeśli zostanie zaplanowany na komputerze ze starym procesorem. Dlatego w przypadku małych testów staraj się uzyskać średni czas działania wynoszący 30 sekund. Celuj w średni czas biegu wynoszący 2 minuty i 30 sekund w przypadku średnich testów.
Zmniejsz liczbę próbek i zwiększ tolerancje na potrzeby uczenia
Wolno działające testy odstraszają autorów. Trening biegowy w testach może być bardzo powolny. Preferuj wyższe tolerancje, aby móc użyć mniejszej liczby próbek w swoich testach i utrzymać je wystarczająco szybko (maksymalnie 2,5 minuty).
Wyeliminuj niedeterminizm i płatki
Napisz testy deterministyczne
Testy jednostkowe powinny zawsze być deterministyczne. Wszystkie testy działające na TAP-ie 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 znajduje się kilka punktów do rozważenia.
Zawsze zaszczepiaj dowolne ź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 niestabilne, sprawia to, że wszystkie testy są powtarzalne. Różne sposoby ustawienia niektórych nasion, które mogą być konieczne 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ę skóry. Zwłaszcza w przypadku korzystania z wielu wątków używanie trybu uśpienia do oczekiwania na inny wątek nigdy nie będzie deterministyczne. Dzieje się tak dlatego, że system nie jest w stanie zagwarantować jakiejkolwiek kolejności wykonywania poszczególnych wątków czy procesów. Dlatego preferuj deterministyczne konstrukcje synchronizacji, takie jak muteksy.
Sprawdź, czy test jest niestabilny
Płatki powodują, że policjanci i programiści tracą wiele godzin. Są trudne do wykrycia i trudne do debugowania. Chociaż istnieją zautomatyzowane systemy wykrywające łuszczenie się, muszą one zgromadzić setki przebiegów testowych, zanim będą mogły dokładnie odrzucić testy. Nawet jeśli wykryją, dodadzą Twoje testy do listy odrzuconych i zasięg testów zostanie utracony. Dlatego autorzy testów powinni podczas pisania testów sprawdzić, czy ich testy nie są wadliwe. 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 umieszczanie wszystkich generatorów liczb losowych używanych w celu maksymalnego ograniczenia ł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 powinieneś używać TensorFlowTestCase. TensorFlowTestCase jest zdefiniowany tutaj: tensorflow/python/framework/test_util.py
Napisz testy hermetyczne
Testy hermetyczne nie wymagają żadnych zewnętrznych zasobów. Są wypełnieni wszystkim, czego potrzebują i po prostu uruchamiają wszelkie fałszywe usługi, których mogą potrzebować. Wszelkie usługi inne niż testy są źródłami niedeterminizmu. Nawet przy 99% dostępności innych usług sieć może działać nieprawidłowo, odpowiedź RPC może być opóźniona i może zostać wyświetlony niewytłumaczalny komunikat o błędzie. Usługami zewnętrznymi mogą być między innymi GCS, S3 lub dowolna witryna internetowa.