Тестирование программного обеспечения
TDD — мои ошибки и способы их исправления
Вещи, которые я узнал со временем…
Я наткнулся на это отличное видео [1] на TDD. В этом видео есть несколько замечательных моментов, которые перекликаются с моим опытом.
Я собираюсь поделиться этими моментами более подробно, добавив свой опыт.
Изолированное выполнение тестов
Когда мы пишем тесты, каждый тест должен выполняться изолированно и не должен влиять на другие тесты.
Пример №1 — Тестирование слоев DAO
Типичным примером может быть тестирование уровня DAO, где для каждого теста требуется база данных.
Если тесты используют одну и ту же базу данных, это приводит к некоторому недетерминированному поведению и, следовательно, к случайным провалам тестов.
Таким образом, решением будет использовать отдельную базу данных для каждого модульного теста. Здесь идет псевдокод.
Pseudocode 1. Start unit test 2. Create a database 3. Populate test data 4. Test functionality 5. Tear down the created database.
Пример №2 — Мокинг
Мы используем насмешки для тестирования объекта, например service layer
, который зависит от дорогого ресурса, скажем, DAO layer communicating with a database
.
Если вы поделитесь своим фиктивным объектом среди модульных тестов, это снова приведет к недетерминированному поведению. В результате у нас случайным образом проваливаются тесты.
В следующем примере два теста разделяютservice
, а также фиктивный объект.
Не делайте этого.
class MyServiceSpec extends AnyWordSpec with MockFactory ... { private val dao: MongodbProductDAO = mock[MongodbProductDAO] private val myService = new MyService(dao) "MyService" should { "my first test" in { ... } "my second test" in { ... } } }
Сделайте это вместо этого.
class MyServiceSpec extends FixtureAnyWordSpec with MockFactory with Matchers { override type FixtureParam = MySerivce override def withFixture(test: OneArgTest): Outcome = { withFixture( test.toNoArgTest( new MySerivce(mock[MongodbProductDAO]) ) ) } "MyService" should { "my first test" in { service => ... } "my second test" in { service => ... } } }
Каждый тест будет иметь свой собственный служебный объект и, следовательно, имитируемый объект.
Красно-зеленый рефакторинг
Вам может быть интересно, что red-green-refactor = test driven development
. Я бы сказал да, но я узнал кое-что новое из этого видео [1].
Вот оно.
1. Write a behavior that fails even without writing any implementation -> (RED). 2. Write (even ugly) code (without caring about design patterns etc) to make the behavior working -> (GREEN) 3. Do refactoring. This is a cleanup process & using design patterns comes here.
Это значит:
- Можно написать уродливый код, чтобы передать заданное поведение и очистить код (рефакторинг/использование шаблонов проектирования и т. д.) позже.
- (ВАЖНО) Рефакторинг не должен нарушить наш тест. Если да, то что-то серьезно не так либо с реализацией, либо с поведением.
Тестирование поведения, а не реализации
Как следует из названия, наш тест не должен быть тесно связан с деталями реализации.
Для нас это может показаться очень очевидным, но поверьте мне, я тоже совершал эту ошибку.
Если бы я тестировал поведение, я бы не тестировал приватные методы. Обсуждение тестирования приватных методов вы найдете здесь.
Чем больше мы добавляем тестов к деталям реализации тестирования, тем больше мы добавляем тесную связь между деталями тестирования и реализации. Это означает, что когда мы рефакторим код, наши тесты начинают давать сбои. На мой взгляд, это плохой дизайн.
Медленное выполнение теста означает медленную обратную связь
Скорость в тестах имеет значение. Медленное выполнение теста означает медленную обратную связь, т. е. то, работает ли данное решение идеально, не нарушая другие тесты.
Здесь я не имею в виду (обязательно) отдельные тесты, но когда весь конвейер CICD работает для вашего конкретного решения.
История о медленном выполнении тестов
В работе мы используем генераторы для генерации поддельных данных. Это замедляет выполнение теста, поскольку он запускает один и тот же тест для разных наборов данных.
Это означает, что если у нас есть 2 теста, каждый из которых выполняется 4 раза, то всего будет 8 выполнений. Большее количество казней требует больше времени.
Это замедляет весь конвейер CICD и, следовательно, приводит к медленной обратной связи.
Ресурс
[1] https://www.youtube.com/watch?v=EZ05e7EMOLM
Спасибо за прочтение. Если у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь задавать их в разделе комментариев.
Если вам понравился этот пост, вам также могут понравиться следующие посты.
- Аутентификация и авторизация токенов на предъявителя
- Мониторинг производительности Postgres с помощью пользовательских запросов
Want to connect? Facebook | LinkedIn | Twitter