Pull to refresh

Comments 24

В общем все правильно но пару маленьких замечаний.
1) Статья некая обзорная лекция о тестировании применимая думаю на любой платформе и любом языке.
Зачем в заглавии слово Android?
2) Скорее вопрос. Я согласен с определениием юнит тестов.
Но скажите пожалуйста у Вас в фирме при написании тестов действительно «Чаще всего в качестве юнита выбирается какой-либо класс».
Думаю, что это скорее исключениие. Реально выбирается модуль состоящий обычно из множества классов.
Ну для примера можно скачать с GitHab нескольно приложений в который есть тесты.
Специально я не исследовал, но на вскидку не нашел ни одного в котором тестировался бы каждый класс отдельно.
Почему я на это обратил внимание?
В фирме где я работаю начитались таких утверждений и считают это образцом.
Ну а так как классов у нас тысячи (порядка 5 000) и в каждом классе минимум пять функций,
то написание Юнит тестов это хотя и надо, но непозволительно долго.
3) Утверждение что Написание тестов значительно увеличивает время разработки справедливо в первую очередь если UnitTest пишется на объекты низкого уровня.
Если разработчик не парится по поводу размера юнита, а пишет тесты исходя из здравого смысла, то возможно даже и сокращает.

Юнит тесты тестируют именно поведение классов. Модули, состоящие из множества классов тестируют интеграционными тестами.


Классический подход прост. Сначала пишем тест, потом пишем код, поведение которого проверяется в тесте. Ну а поведенческий код в объектно-ориентированной парадигме обычно упаковывается в класс. Поэтому юнит тесты обычно тестируют методы одного класса.

добавлю важный момент — юнит тесты должны быть максимально независимы от окружения.

К примеру, мы можем иметь один класс, но он будет работать с базой/файловой системой и если у нас базай/файловая система как-то не замокана то получим в итоге интеграционный тест.

Самый типичный пример — тестирование ActiveRecord моделей унаследованных от базового ActiveRecord. Тест как-бы будет проверять один класс, но по факту мы будет тестировать заодно и кучу компонетнов ORM.

Если не совсем понятно к чему я клоню, могу привести примеры кодом.

ActiveRecord на мой взгляд — антишаблон. Модель данных не должна зависеть от механизмов хранения.

Юнит тесты тестируют именно поведение классов. Модули, состоящие из множества классов тестируют интеграционными тестами.

Вот именно с этим я и спорю Интереса ради почитайте искусство автономного тестирования автор Рой Ошероув
Вот определение UnitTest которое он дает

Единица работы
— это совокупность действий от момента вызова какого-то
открытого метода в системе до единственного конечного результата, замет-
ного тесту системы. Этот конечный результат можно наблюдать, не исследуя
внутреннее состояние системы, а только с помощью открытых API и поведе-
ния.
Автономный тест – это часть кода, которая
вызывает единицу работы и затем проверяет ее конечный результат. Если
предположения о конечном результате не подтверждаются, считается, что
автономный тест завершился неудачно. Объектом автономного тестиро-
вания может быть как единственный метод, так и совокупность нескольких
классов.

Баги часто встречаются именно при взаимодействии классов. Если несколько классов предоставляют некую сущность которую можно протестировать вместе то лучше так и делать. И если вы считаете этот тест интеграционным, ну это ваше дело.
Цель За меньшее время написать тесты имеющие наибольшее покрытие.
вот к стати статья в которой автор рассуждает об интеграционных и юнит тестов https://habrahabr.ru/post/275249/

Меньше всего мне хочется спорить с кем бы то ни было о вкусах. У меня вообще нет такой цели: "За меньшее время написать тесты, имеющие наибольшее покрытие" потому что я всегда сначала пишу тест, а потом код. Я сначала пишу тесты потому что это:


  • Улучшает внутренний дизайн и архитектуру программной системы
  • Удешевляет и упрощает последующее изменение кода
  • Порождает "исполняемую" документацию по коду, которая никогда не врет
  • Упрощает повторное использование кода
  • Снижает стоимость эксплуатации программной системы
К стати я тоже стараюсь сначала писать тесты только мотивация у меня совсем не такая.
Я работаю скорее по BDD начинаю проектировать на каком то достаточно высоком уровне и постепенно спускаюсь к более низкоуровневым конструкциям, но до тестов одного класса доходит редко обычно в случае если там какая то особо заумная функция есть.
И мотивация только одна сделать свою работу как можно лучше. А все что Вы перечислили это только дополнительные стимулы.

Главная задача для меня — получать удовольствие от программирования.


Что означает "начинаю проектировать на каком-то достаточно высоком уровне"?

Главная задача для меня — получать удовольствие от программирования.

Как бы это… тут мы как представители древнейшей профессии (приятно, да еще и деньги платят не малые).
А по поводу высоком уровне. В Зависимости от задачи отвечаю на вопросы
Что должно произойти в системе?
Как это может быть исполнено?
Ну и пишу соответствующие тесты и интерфейсы которые пока мокаются.
в дальнейшем заменяю моки реализацией.
В общем BDD
Утверждение что Написание тестов значительно увеличивает время разработки справедливо в первую очередь если UnitTest пишется на объекты низкого уровня.
Если разработчик не парится по поводу размера юнита, а пишет тесты исходя из здравого смысла, то возможно даже и сокращает.

Я просто обожаю эту фразу, «Написание тестов значительно увеличивает время разработки». Всегда, когда слышу ее возникает чувство, что люди которые такое говорят не писали приложений сложнее автогенерируемых.

Конечно, юные разработчики, которые и программировать нормально еще не умеют, будут затрачивать больше времени на разработку при написании тестов.
Но это и не секрет, что джуны все делют или дольше или не качественнее.

Если же говорить про разработчиков, которые понимают философию тестирования и уже разобрались как пользоваться тестовым фреймвокром, то при разумном написании тестов проиводительность только повысится.

На пимере веб-приложения.
Мы можем иметь класс, который обрабатывает данные с формы и выдает какой-то результат для дальнейшей обработки следующему классу.

Без теста мы можем вводить данные на форму, отправлять ее и смотреть что получим(может еще и дебажить в то же время).

С тестом мы просто введем нужные нам данные при написании теста и будем его запускать каждый раз когда хоти проверить правки.

От одного только сокращения времени на перезагруке страницы, вводде данных и ожидании ответа сервера можно выиграть не мало времени.

P.S. не подумайте, что я с Вами спорю — наоборот, поддерживаю Ваше высказывание
пишет тесты исходя из здравого смысла, то возможно даже и сокращает.
Так то оно так но есть к сожалению троглодиты которые книжек ни читают и тесты не пишут
Классная лекция. Узнал много интересного.
Я пишу под .Net, но тут одна фигня, поэтому задам парочку вопросов :)

1. «толстые» тесты лично у меня как правило получаются когда метод делает много операций, типа «ReadAndApply»:
public DoMagic()
{
DoAbra();
DoCadabra();
}

Но внутренние методы не хочется показывать наружу тк их могут использовать (а не надо). Поэтому приходится делать наследника и тюкать их как protected. Что иногда «доставляет». Или делать 100500 хелперов.

2. Как не писать фригильные тесты это целая наука и нигде толком не описано. В один прекрасный день одно небольшое изменение можеть сломать столько тестов, которые компилиться откажутся, не то, что проходить :) Постигается только потом и болью (и переписыванием фикстур).

3. Вопрос знатокам. В .Net чтобы мокить класс должен или быть абстрактным или интерфейсным.
В MVVM понятно как тестировать модель. InMemory база и все летает. Тестировать ViewModel даже на такой датабазе кажется странной идей поэтому у меня каждая модель имплементирует свой интерфейс и он соотв. мокится. Это нормально или я усложняю?

Ответы на ваши вопросы есть в книгах Роберта Мартина, Кента Бека и "банды четерех" (Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес).

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

Вот как раз открыл статью что бы прочиать о тестировании интент-сервисов, бродкастов, handleMessage, EventBus и прочей асинхронщины, но не судьба…

А в чем проблема то с тестированием "асинхронщины"?

Тем что проверять работу посылки сообщения порой необходимо в другом потоке а иногда и другом процессе.

Не вижу особых проблем с реализацией подобных тестовых сценариев. Может конечно у вас какой-то неожиданно особый environment, без примеров кода сложно судить.

Тесты — зло.
Иногда, в случае активного использования вашего модуля кем-то другим — необходимое.
С тестами есть следующие проблемы:
  1. на их написание уходит время
  2. на их вдумчивое продумывание уходит крайне длительное время
  3. они ломаются вместе с изменением API
  4. их использование, даже после того, как они написаны — отнюдь не бесплатно

Понятно, что если вы реализуете какую-нибудь матричную арифметику, в которой может быть куча corner cases, то без большого объёма всеобъемлющих тестов не обойтись — но часто ли вы изобретаете такие велосипеды?
При этом, многие из тестов — абсолютно не нужны!
Есть ли смысл каждый раз проверять что 2+2=4, если любая ошибка в этом выражении — достаточно тривиальная, чтобы её поймал юнит-тест — и так будет сразу же обнаружена при использовании программы?
Поэтому для себя я решил — тесты у меня в программах должны создаваться практически полностью автоматически, причём только тогда, когда уже выявлено наличие в коде бага — и написан тест должен быть так, чтобы как раз выяснить детали проявления этого бага.

Для этого синтаксис тестов у меня полностью совпадает с синтаксисом системы логирования: запустил программу в debug-режиме, скопировал кусок дебажного вывода в тестовый проект — и тест готов.
C первыми тремя пунктами солидарен с вами.
В четвертом не понятно, что конкретно имелли ввиду? Процессорное время?
Для этого синтаксис тестов у меня полностью совпадает с синтаксисом системы логирования: запустил программу в debug-режиме, скопировал кусок дебажного вывода в тестовый проект — и тест готов.

Можно подробнее, что подразумеваете под тестовым проектом?
В четвёртом не понятно, что конкретно имели ввиду? Процессорное время?
И оно тоже (полная проверка кода с множественными ветвлениями может занимать очень длительное время, если вообще осуществима), но больше время того, кто тестирует, и того, кто получает с него отчёты. Чаще всего эта проблема — как следствие пункта 3.
Можно подробнее, что подразумеваете под тестовым проектом?
В моём случае — шаблонный проект QT Unit test. Отфильтрованные логи вызова функций для проверяемого класса вставляются прямо внутрь функций test_case().

Не видел еще ни одного проекта, где к примеру модульные тесты занимали бы хоть сколько-нибудь значительное время. Около 3000 модульных тестов в проекте бэкенда, написанного на C# с использованием ASP.NET занимало несколько секунд.

НЕ согласен по всем пунктам.
1 и 2 Как уже писалось в обсуждении этой статьи тесты при правильном использовании экономят время даже во время разработки.
3 Если изменилось API. Нарушение от принципа открытости закрытости. Ну как бы бывает конечно такая необходимость но сравнительно редко. И тогда новое API Надо тестировать.
4 Да не бесплатно за все надо платить
Тесты гоняются на сервере и жрут сволочи электричество ( посчитайте сколько интереса ради) Ну и иногда находят ошибки которые увы надо править. Что то же занимает время и иногда раздражает. Ну вроде бы все сделал а тут ошибка за ошибкой.

А вот про авто написание тестов это интересная мысль. Хотя и не понял толком что там происходит.
по Вашим словам я понял что вы ухитрились так написать Лог что бы он имитировал вызов функции с параметрами которые были преданы.
Это должно быть сделано автоматически Вызвал в начале каждого метода функцию типа Log.AutoTraiсe и готово
Если это на Net то Если можно дайте образец кода
Sign up to leave a comment.