Zdarza się, że test automatyczny potrzebuje tymczasowych struktur na dysku maszyny, na której jest wykonywany, na przykład katalogu na pliki zapisywane w trakcie pracy przez testowany program. Tego typu obiekty należy tworzyć w przemyślany sposób, żeby uniknąć niepotrzebnych zakłóceń wykonania testów, spowodowanych na przykład przez błędy pochodzące od systemu operacyjnego przy próbie utworzenia katalogu o nazwie, która już istnieje. Na szczęście język Python
oraz framework Pytest
posiadają mechanizmy, które bardzo takie zadanie ułatwiają.
Moduł tempfile
Python
ma wbudowany moduł tempfile
, który służy bezpiecznemu zarządzaniu tymczasowymi katalogami i plikami. Jego ogromną zaletą jest to, że jego funkcje działają zawsze tak samo, niezależne od platformy i sposobu działania danego systemu. Moduł ten domyślnie umieszcza tworzone struktury w lokalizacjach, które typowo służą do przechowywania tymczasowych danych (Windows: C:\Users\<username>\AppData\Local\Temp
, Linux: /tmp
) oraz dba o nadawanie im unikatowych nazw. Pozwala również takie struktury automatycznie usunąć po ich wykorzystaniu, a to dzięki zastosowaniu w jego klasach protokołu managera kontekstu.
Manager kontekstu
Manager kontekstu (ang. context manager) to konstrukcja w języku Python
, która pozwala na łatwe i bezpieczne zarządzanie zasobami, w tym przede wszystkim na ich uwolnienie w momencie zakończenia lub przerwania kodu, który z nich korzysta. Manager kontekstu jest definiowany jako blok kodu rozpoczynający się słowem kluczowym with
. Jednym z jego najbardziej popularnych zastosowań jest otwieranie plików z wykorzystaniem funkcji open
:
1 | with open(filepath) as f: |
Powyższy kod odczytuje zawartość pliku znajdującego się w ścieżce spod zmiennej filepath
. Zastosowanie managera kontekstu zapewnia, że plik zostanie zawsze automatycznie zamknięty (a powiązane z nim zasoby uwolnione), niezależnie od tego, czy kod znajdujący się w bloku with
wykona się prawidłowo, czy na przykład spowoduje wyjątek.
Tip: więcej na temat managera kontekstu znajdziesz w świetnym artykule Python with Context Managers na blogu Jeffa Knuppa oraz w dokumentacji języka
Python
na stronach opisujących Context Manager Types i moduł contextlib.
W przypadku modułu tempfile
, konstrukcję managera kontekstu ze słowem kluczowym with
możemy wykorzystać do automatycznego usunięcia tymczasowych danych kiedy przestaną być potrzebne. Poniższy przykład pokazuje tego typu operację wykonaną na tymczasowym katalogu:
1 | import tempfile |
Manager kontekstu wewnątrz fixture
We frameworku Pytest
tymczasowy katalog najlepiej utworzyć w dedykowanej do tego celu fixture, która przekaże informacje o ścieżce katalogu do potrzebujących go funkcji testowych.
Warto pamiętać, że fixture standardowo może zawierać kod, który zostanie wykonany dopiero wtedy, gdy wszystkie korzystające z niej funkcje testowe zakończą działanie. To tak zwane kroki teardown, które umieszcza się po słowie kluczowym yield
, zwracającym wartość z fixture. Możemy to wykorzystać do posprzątania tymczasowych struktur po testach.
Tip: więcej na temat kroków teardown znajdziesz w moim poprzednim artykule Pytest - zakres dla fixture i kroki teardown
Można powiedzieć, że wykonanie fixture zatrzymuje się na linii yield
i jest kontynuowane po zakończeniu testów. W połączeniu z managerem kontekstu pozwala to na stworzenie bardzo ciekawej konstrukcji, w której po słowie kluczowym yield
nie ma co prawda żadnego kodu, ale w rzeczywistości, zaraz po wykonaniu wszystkich zależnych testów, wykonywane są kroki zamykające managera kontekstu, które to na przykład… usuwają niepotrzebny już tymczasowy katalog :)
Jest to pokazane w poniższym bardzo prostym przykładzie, w którym funkcja testowa sprawdza, czy w utworzony przez fixture temp_dir
tymczasowy katalog rzeczywiście istnieje w systemie plików.
1 | import os |
Jeśli chcesz, możesz pobrać projekt zawierający powyższy kod i definicję koniecznych zależności klonując repozytorium:
git clone https://gitlab.com/qalabs/blog/pytest-tempdir-example.git
Jeśli nie masz Gita
, możesz pobrać zip
ze strony projektu https://gitlab.com/qalabs/blog/pytest-tempdir-example i rozpakować w dowolnym katalogu.
Współdzielenie struktur przez testy
Czasem może wystąpić sytuacja, w której bardziej optymalne lub wręcz konieczne będzie, aby testy współdzieliły tymczasowe struktury, na przykład wykorzystywały ten sam katalog. Aby to osiągnąć, można skorzystać z mechanizmu frameworku Pytest
definiowania zakresu dla fixture.
Domyślny zakres to pojedyncza funkcja testowa. Oznacza to na przykład, że nowy tymczasowy katalog zostanie utworzony na początku każdego testu i usunięty zaraz po jego zakończeniu. Ale dla fixture można też podać szerszy zakres, dzięki czemu ten sam katalog będzie dostępny na przykład dla wszystkich testów w danej klasie, a nawet w całej sesji testowej (wszystkie możliwe wartości zakresu dla fixture to function
, class
, module
, package
, session
).
Tip: więcej na temat definiowania zakresu dla fixtures znajdziesz w moim poprzednim artykule Pytest - zakres dla fixture i kroki teardown
Podsumowanie
Jak widać, dzięki funkcjonalnościom wbudowanym w język Python
(moduł tempfile
, manager kontekstu) oraz we framework Pytest
(kroki teardown i zakres dla fixtures) zarządzanie tymczasowymi katalogami i plikami na potrzeby testów jest bardzo proste i efektywne - do tego stopnia, że przedstawiony powyżej minimalny działający przykład zawiera… zaledwie dziewięć linijek kodu.