nepster-web
@nepster-web

Как добиться независимости в тестах (phpunit)?

Начну с небольшой предыстории:
У меня есть слоистая архитектура, оперирующая следующими понятиями:
Request, ViewModel, Repository, Presenter

В нескольких словах это можно описать так описать так:
- Request - делает запрос в некий источник данных (например файл, база или Api)
- Далее с помощью некого билдера возвращает ViewModel (класс с геттерами)
- Presenter - некая обертка над ViewModel, которая модифицирует методы для видов.
- Repository особо в описании не нуждается.

При тестировании, я создал мок для Request, подсунул определенные данные и стал тестировать.
Проблема обнаружилось в том, что если вдруг тест падает на ViewModel, то это затрагивает другие тесты. Тоесть если не работает ViewModel, автоматически падают тесты Request, Presenter и Repository

Цепочка вызовов идет примерно так:
Repository -> Request -> ViewModel -> Presenter

Соответственно, если упадет тест более низкоуровневой вези, например Request, упадут все тесты, которые используют данный Request.

Я очень сильно сэкономил на коде тестов, тоесть вроде все затестированно, может выполнятся в произвольном порядке, а вот нюанс с падением 4 тестов вместо одно, сомнительный.

Подскажите, пожалуйста, нужно ли мокать каждый класс и писать в 10 раз больше кода, чтобы такого не происходило, если да, то почему ?
  • Вопрос задан
  • 460 просмотров
Решения вопроса 2
@kn0ckn0ck
Продюсер
Не стоит смешивать модульные тесты и интеграционные (или функциональные). Цель модульных тестов проверить работу одного модуля (класса, например). В этом случае все его зависимости мокаются. Целью интеграционных тестов является проверка взаимодействия цепочки модулей (сервисов, с БД и т.п.) друг с другом.

То что вы написали похоже на интеграционные тесты. Очевидно, что такие тесты будут падать, если что-то не работает в любом месте всей тестируемой цепочки модулей. И это не связано с изоляцией тестов друг от друга.

То есть правильно было бы сформулировать вопрос таким образом: "какой процент покрытия модульными тестами будет достаточным для моего кода?" Обычно останавливаются где-то на 70-80%

Также очевидно, что 100% работающих модульных тестов не гарантирует работу интеграционных тестов или функциональных. Поэтому необходимо писать и те и другие.

Я бы не стал фанатично закрывать все методы классов тестами, а только те, в которых имеется высокая цикломатическая сложность, либо которые скорее всего будут меняться. Короче, нет большого смысла в тестировании примитивных/стабильных участков кода.
Ответ написан
Комментировать
lxsmkv
@lxsmkv
Test automation engineer
я в основном делаю ui тесты и у меня некоторые тесты связаны друг с другом. конечное состояние первого теста является начальным сосотоянием следующего. Например первый тест "зайти в формуляр" а следующий за ним "ввести данные в первую строку" а затем тест "ввести данные во вторую строку" а потом "нажать на отправку данных" а потом "тест сообщения об отправке". Естественно если первый тест завалится, завалятся и все после него, хотя реальная проблема только одна. Единственный серьезный недостаток такого подхода, это возможное перекрытие ошибок. Т.е. в билде может сломаться заход в формуляр, и отображение сообщения об отправке. Вторая проблема будет перекрыта первой.
Но меня устраивает такой подход по следующим соображениям.
- Если что-то сломалось в приложение надо в первую очередь чинить приложение, а не писать тонны дополнительного тестировочного кода.
- Тестировочный код тоже код, чем его больше тем больше вероятность сделать ошибку в нем.
- Лучше потратить время на увеличение покрытие пользовательских сценариев, чем на создание красивой и каноничной автоматизации.
- Зависимые тесты выполняются быстрее, (а у нас время тестирования билда во всех вариациях уже перевалило за 16 часов)
- Чинить по одной ошибке за раз - хорошо, пытаясь починить сразу несколько можно сломать что-то еще.

Для того чтобы такой подход работал более менее чисто, я например стараюсь сдвигать все проверки в конец теста, уменьшая таким образом вероятность того что вылетевшее исключение не доведет приложение до нужного состояния для последующего теста.

Но это все не по вашему вопросу. По вашему вопросу - да, писать тонны моков (не нужно). У меня например есть тесты которые проверяют как данные от железа передаются в api графического клиента. при проверке фунцкионала графического клиента, я опираюсь на корректную работу api графического клиента, поскольку им пользуюсь. Так же я опираюсь на корректную работу виджетов. И если какие-то условия не будут работать многие ui тесты посыплются. Мы найдем причину и устраним ее. Чтобы в таком случае падал только один тест, который специально сделан для нахождения этой ошибки, вам придется отвязать тестируемую компоненту от всех внешних зависимостей. Теоретически написав столько же кода сколько в самом приложении. Вы готовы к этому?

P.S.: стоит добавить: наш тест-фреймворк выполняет тесты в алфавитном порядке и не поддерживает параллельное выполнение в принципе.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы