Pull to refresh

Comments 19

UFO just landed and posted this here

Тесты не виноваты, что их написал председатель клуба тавтологий. У него, наверное, и комментарии в коде такие, что прям думаешь: "вот и зачем он из названия метода подчёркивания убрал и пробелы поставил?"

UFO just landed and posted this here

Скорее, "I know this feel, bro" (с)

def test_random_real():
    assert 0 <= get_thing() < 10

Я думаю это плохой тест. Предположим произошла ошибка и рандом стал возвращать числа до 10 включительно. О такой ошибке тест может сообщить очень не скоро.

Плохой, да.
Например, вы ещё и закладку не найдёте, если на каждой итерации №№317-350 будут выдаваться нули.
Но тут важно ведь не слепое следование примеру...

В данном примере, если не мокать получение рандомного числа — то, логически, мы уже начинаем тестировать метод получения рандомного числа. А это задача довольно нетривиальная, нужно проводить много испытаний и смотреть распределение. Не лучше ли реализовать тестирование получения рандомного числа в классе получения рандомного числа?
А этот метод я бы вообще тестами не покрывал, ибо он не содержит логики. А если бы он содержал логику, завязанную на рандомные числа, то я бы предпочел замокать рандом, тем самым упростив тест и сделав его стабильным. Здесь, очевидно, нужен компромис.
Что я хотел сказать: данный пример, на мой взгляд, не защищает точку зрения, изложенную в статье :)

Да, пример в статье не идеальный, согласен

А какой тест вы можете предложить для того, чтоб такую ситуацию «выловить» надёжно и за предсказуемое время? Ну, такой, который при этом тестирует сколько-нибудь «общее» поведение метода/объекта, а не какую-то очень конкретную деталь имплементации.

Если что, это не «спервадобейся», а вполне серьёзный вопрос — мне интересно узнать.
Как я уже написал раньше, если бы я тестировал получения ранодомного чесла — я бы вызвал метод большое количество раз (например 10 тысяч), затем проверил бы, что все числа находятся в заданных пределах и что распределение близко к равномерному. При этом, разумеется, тест может оказаться не очень стабилен (все таки это рандом). Но стабильность можно улучшить увеличив число вызовов.
В некоторых ситуациях я пользуюсь принципом (как сам его называю) «слабого» или «достаточного» доказательства.
У нас в приложении есть плеер, который имеет режим смешивания. Необходимо убедиться что функция смешивания работает. Сначала нужно определить, что для меня будет достаточным доказательством. Как бы я тестировал руками? Выбрал бы трек из середины списка, включил режим смешивания, нажал на кнопку «следующий трек» и проверил что новый трек не является следующим по списку, и не является треком который мы изначально выбрали. Т.е. он «любой другой, кроме». Мы не проверяем работу рандомизатора, мы проверяем функцию приложения, на соответствие заявленному поведению. Этот тест не дает нам абсолютной гарантии верного поведения, потому что рандомизация может перестать работать после второго нажатия по кнопке «след. трек». Но оно достаточное, чтобы двигаться дальше по покрытию. Усилить тест можно потом, в случае необходимости, если будут прецеденты.
Чтобы не залезть в дебри и не начать тестировать совсем не то что нужно, в совершенно неоправданных объемах, а быстро двигаться вперед, порой помогает вслух спросить себя «что является предметом теста?».
Отчасти суть моего вопроса сводится к тому же — если тестировать поведение, о чём, вроде как, и говорится в данной статье, мы не можем именно при таком подходе к тестированию гарантировать корректность работы имплементации в краевых случаях, которые нельзя обеспечить исключительно подачей каких-то специфичных входных данных, как в случае с рандомом тут. То есть это в целом можно (а в каком-то смысле и нужно) тестировать, но мне кажется, это уже другой аспект проверки приложения на работоспособность.
В последнее время люди части пишу и рассказываю про мутационное тестирование. Как один из способов борьбы с такими тестами — должен подойти отлично.
bbidox Артем, вот скажите, вы стали править тесты, чтобы избавить их от тавтологий. Результаты тестов изменились? К лучшему? К худшему? Вот о чем нам бы всем интересно было узнать. Может приведете интересный пример из ваших UI тестов? Потому что надуманный пример из статьи, действительно, не очень нагляден. А UI тесты как правило очень наглядны.

Ответ будет длинным.
Совсем избавить UI-тесты от тавтологий, наверное, не получится. Причина как в раз в том, что же мы хотим проверить — мы хотим убедиться что поведения правильное или что поведение соответствует ожидаемому? Правильное поведение должно быть протестировано вручную или с помощью TDD, с привлечением документации или хотя бы с помощью здравого смысла и постановщика задачи. Протестировать то, что ожидаемое поведение не нарушено — это, по сути, регрессионное тестирование. В части задач (например, ежедневные выкладки) целенаправленно тесты пишутся именно таким образом.


Но можно убрать тавтологию в компонентах, на которые мы делим тест или проверяем. Например, мы знаем, что определённая компонента подвергается постоянным изменениям. Так ли нам важно проверять, что элемент <div id="test">new text</div> является div-ом, не достаточно ли проверять элемент #test? Кажется очевидным, но это пример простой. В реальных тестах всё довольно сложнее.


Ну, например, выпилил проверку того, всплывающее сообщение с каким текстом показывается после той или иной операции. Там нет текста, соответствующего ошибке? Значит, тест будет считать, что операция завершилась успешно и продолжать своё выполнение: ему нужно убедиться, что, например, покупка прошла и поменялись балансы. Поменялись? Отлично.
А проверку того, какое облачко будет показываться после покупки (например) — мы будем делать в тепличных условиях и проверять будем только это.


Короче. Порой надо проверять компоненты, а порой — сценарии. Если нужен сценарий, то выпиливайте подробную проверку компонент.
По-моему, так.

у меня как-то тимлид спрашивал в чем причины необходимости поддержки UI тестов. И одним из пунктов я тогда назвал то что тестируемое приложение является Runtime Environment для самих тестов. (и еще подумал тогда, что это довольно трудное для понимания заявление) Т.е. в тесте мы (слепо) полагаемся на правильную работу некоторых компонент, например, что кнопка которую нам нужно нажать активна. Кнопка в сценарии не является предметом теста, но ее правильная функцкия необходима для работы теста.
Меня и по сей день регулярно посещает мысль, что компоненты должны быть тоже протестированы перед тем как гонять сценарии. Но наша задача проверить работу нашей части приложения, а не проверять фреймворк и пр. Поэтому так и живем.
А вот еще нашел интересный примерчик
исходник на гитхаб
    public void testCreateWithNoInitialText() throws Throwable {
        View createMenu = view(id.m_apply);
        assertFalse(createMenu.isEnabled());
        EditText content = editText(id.et_gist_content);
        focus(content);
        send("gist content");
        assertTrue(createMenu.isEnabled());
}


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

Какая польза от первого утверждения? Он оберегает тест от ситуациии когда тест пройдет несмотря на то что страница была изначально активна. Значит посылание текста в активную страницу тестом не распознается. Вопрос: а является ли это ошибкой? Если да, то это должно быть протестированно отдельно. Если нет, то это входное условие лишнее.
Sign up to leave a comment.