Historia pytania
Ręczne sprawdzanie logiki biznesowej w SQL (na poziomie procedur składowanych, funkcji, wyzwalaczy) prowadzi do błędów, które ujawniają się dopiero w produkcji. Przez długi czas testowanie scenariuszy SQL było nieformalne i niestandardowe. Jednak rozwój technologii CI/CD wymaga zautomatyzowanych testów także dla kodu SQL.
Problem
Większość programistów ogranicza się tylko do testów na poziomie aplikacji. Brak sprawdzania samych procedur i funkcji SQL prowadzi do defektów, które nie są objęte żadnym pakietem testowym — na przykład przy zmianie logiki UDF lub regresji w raportach.
Rozwiązanie
W procesach roboczych nowoczesnych zespołów organizowane są testy jednostkowe i integracyjne bezpośrednio dla kodu SQL. Do testowania jednostkowego stosuje się frameworki tSQLt (SQL Server), utPLSQL (Oracle), pgTAP (PostgreSQL), a do testów integracyjnych — oddzielne środowiska do uruchamiania tymczasowych baz, stosowania migracji i sprawdzania scenariuszy biznesowych.
Przykład testu jednostkowego na pgTAP:
-- Sprawdzamy wynagrodzenie SELECT plan(2); SELECT is( (SELECT calc_salary(1)), 1000, 'Wynagrodzenie dla użytkownika 1 poprawne' ); SELECT isnt( (SELECT calc_salary(2)), 0, 'Wynagrodzenie użytkownika 2 nie jest zerowe' ); SELECT finish();
Kod testu integracyjnego dla CI/CD:
psql -U user -d testdb < migrations.sql psql -U user -d testdb < test_data.sql psql -U user -d testdb -c "SELECT * FROM my_procedure_test();"
Kluczowe cechy:
Czy można obejść się testami automatycznymi tylko na poziomie aplikacji, jeśli procedury są duże? Nie, ponieważ testy UI/API nie gwarantują poprawności działania logiki SQL (na przykład, błędne warunki wewnątrz funkcji składowanych lub naruszenia przy aktualizacji danych). Testy jednostkowe powinny obejmować wszystkie wyjściowe gałęzie wykonania w samym kodzie SQL.
Czy wystarczą "ręczne" uruchomienia skryptów testowych — przecież w bazie jest mało zmian? Nie wystarczą, nawet w małych projektach pojawiają się błędy po zmianach w schemacie lub logice. Automatyzacja testowania w procesie CI zmniejsza czynnik ludzki i zapobiega regresjom.
Czy można testować tylko "krytyczne" procedury, resztę pominąć? Najlepszym podejściem jest, na ile to możliwe, pokrycie maksymalnej liczby funkcji, szczególnie jeśli w przyszłości kod będzie zmieniać kilka zespołów: nieoczywiste obliczenia i edge-casy najczęściej ujawniają się właśnie w niestandardowych gałęziach.
Podczas poprawy procedury obliczania rabatów testowano ręcznie kilka przypadków, głównych gałęzi logiki nie uwzględniono. W produkcji klienci zaczęli otrzymywać błędne rabaty, rozwiązywanie problemu zajęło kilka dni.
Zalety:
Oszczędność czasu na początku.
Wady:
Straty z ręcznego poprawiania, niedogodności przy dalszym rozwoju i refaktoryzacji.
Opracowano testy jednostkowe z pgTAP dla wszystkich kluczowych UDF i procedur, testy integracyjne są uruchamiane przez CI przy każdym mergu gałęzi. Błędy i regresje są wykrywane przed wdrożeniem.
Zalety:
Stabilność funkcji, możliwość szybkiego dostosowywania logiki biznesowej, minimalne błędy w produkcji.
Wady:
Wymaga inwestycji czasu na rozpoczęcie i utrzymanie bazy testowej.