Тестирование с помощью эталонного вывода
Макконнелл в своей книге “Совершенный код” рекомендует технику создания тестов, которая распространена во проектах с открытым исходным кодом:
Регрессивное (или повторное) тестирование значитально облегчают автоматизированные инструменты сравнения действительных выходных данных с ожидаемыми. Данные, выводимые на печать, легко проверить перенаправив их в файл и сравнив при помощи утилиты diff(1) или другой утилиты сравнения файлов с другим файлом, в который ранее были записаны ожидаемые данные. Если файлы различаются, радуйтесь: вы обнаружили регрессивную ошибку.
Такие тесты называют по разному: тесты на основе эталонного вывода, diff-based тесты, golden-тесты и т.д. Я сталкивался с такими тестами в Tarantool и в PostgreSQL и не в восторге от удобства работы с ними. С другой стороны такие тесты в некоторых случаях удобнее, чем обычные ассерты. Такие тесты часто популярны среди гоферов (Golang).
- contra: write easy, maintainance is hard
- contra: explicit test oracle, test is useless without verified .result file
- contra: fragile because depend on formatting
- contra: one cannot create a test when tested functionality is not ready,
because output is a mandatory part of a test - contra: during review you should inspect both artifacts: source code of a
test and it’s output. It’s double efforts for reviewers. - contra: вывод может меняться между запусками (flaky) id timestamp
- pro: you can easily reproduce a bug with copy-paste test source code in interactive mode
- pro: удобно когда надо большое количество полей проверить
Есть такой тип тестов, их по-разному называют: golden-тесты, тесты с эталонным выводом и др. Суть такая: вывод нового теста сохраняют в файл и при дальнейших запусках этот сохранённый вывод сравнивается с фактическим. Такой подход, например, популярен для SQL тестов (что в MySQL (https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_DEALING_OUTPUT.html), что в PostgreSQL (https://www.postgresql.org/docs/12/regress-variant.html)) - достаточно написать нужные конструкции на SQL, выполнить, убедиться, что вывод корректный и сохранить в файл. Надо только убедиться, что ввод получается всегда детерминированный, иначе добавите себе flaky тестов. Вывод может зависеть от установленной локали в системе (поможет NO_LOCALE=1), от сообщений об ошибках, от времени или даты в выводе и т.д. Такой подход с тестированием противоположен тому, когда используются ассерты для сравнения фактического и желаемого результатов. Ещё такой подход популярен при тестировании сериализации, в Go много таких тестов видел. Обычно аргументация авторов этих тестов такая: “вручную писать тесты для сериализации в JSON — долго, неприятно, и всем лень это делать. При этом баги из-за поломанной сериализации тяжело отлавливать, и обычно они всплывают очень поздно, уже на этапе интеграции с внешними API. Например, переименовали поле в классе, не зафиксировали старое название в коде с сериализацией, и отправили такой документ в сторонний сервис. В лучшем случае он вернёт ошибку, в худшем — будет молча проставлять дефолтное значение в старое поле.”
Расскажите, используете такой подход?
Смотри также:
- MySQL
- MariaDB
- PostgreSQL
- PostgreSQL
- https://vincent.demeester.fr/posts/2017-04-22-golang-testing-golden-file/
- https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter
- https://github.com/jfecher/golden-tests
- https://en.wikipedia.org/wiki/Characterization_test
- https://blog.thecodewhisperer.com/permalink/surviving-legacy-code-with-golden-master-and-sampling
- https://www.artima.com/weblogs/viewpost.jsp?thread=198296
- https://ro-che.info/articles/2017-12-04-golden-tests
- https://kseo.github.io/posts/2016-12-15-golden-tests-are-tasty.html