Я думаю, все здесь считают, что написание юнит-тестов -- это неотъемлимая часть разработки любого более-менее порядочного программного обеспечения, не так ли?
Скажите, пожалуйста, всегда ли вы пишете юнит-тесты? На каких языках вы программируете и какие библиотеки используете для их написания? Делаете 100% покрытие кода тестами или "забиваете" на юнит-тестирование определённых частей проекта?
Я вот, например, до сих пор не могу понять, как можно писать тесты в некоторых случаях. Например, пишу я агрегатор объявлений с avito.ru на Python. Использую при этом фреймворк под названием Scrapy, который абстрагирует меня практически от всего (HTTP-реквесты, переподключения, редиректы etc). Всё, что мне остаётся -- вытащить из результата, переданного в мой callback в виде HTML-страницы, несколько значений, после чего записать их в БД / Excel-файл / etc. Что при таком подходе можно тестировать? Успешность выполнения GET-реквестов и валидность данных, которые прилетают мне в ответе? Так они выполняются где-то в недрах фреймворка, я даже знать-не знаю, какие там методы вызываются и что конкретно происходит. Даже если я посмотрю в исходники и увижу, что на самом нижнем слое находится, например, urllib из стандартной библиотеки Python, то полагаться на недокументированное поведение, на мой взгляд, в данном случае глупо. Успешность записи данных в БД / Excel-файл? Первое вообще непонятно, как зафейлиться может (разве что структура таблиц не совпадает с используемой мной моделью), второе ещё ладно.
В общем, поделитесь советами на тему того, что и как следует покрывать тестами, а что следует оставлять без покрытия. Чем вы руководствуетесь при написании юнит-тестов?
Здравствуйте, FrozenHeart, Вы писали:
FH>Всё, что мне остаётся -- вытащить из результата, переданного в мой callback в виде HTML-страницы, несколько значений, после чего записать их в БД / Excel-файл / etc. Что при таком подходе можно тестировать?
В приведенном примере — разве что выделенное. Хотя полезность тоже под сомнением, т.к. входные данные (структура страницы) может меняться со временем и тесты надо будет дорабатывать. Полезнее в данном случае будет внутри рабочего кода добавить проверки по входу и по выходу (ассерты + исключения / логгирование или как там принято в Питоне).
Здравствуйте, FrozenHeart, Вы писали:
FH>Я думаю, все здесь считают, что написание юнит-тестов -- это неотъемлимая часть разработки любого более-менее порядочного программного обеспечения, не так ли?
FH>Скажите, пожалуйста, всегда ли вы пишете юнит-тесты? На каких языках вы программируете и какие библиотеки используете для их написания? Делаете 100% покрытие кода тестами или "забиваете" на юнит-тестирование определённых частей проекта?
В строгом определении (тестирование метода класса) я юнит-тесты не пишу. Код большей частью алгоритмический, и большинство ошибок не в одном методе, а во взаимодействии сущностей. Поэтому я больше пишу функциональные тесты — которые проверяют функциональность, работу нескольких методов совместно.
Обычно тест у меня обычно специальное консольное приложение, которое принимает имя теста в командной строке и выполняет его. Написал свой test-driver для autotools, который получает список всех тестов, потом запускает их (опционально под valgrind, задаётся в configure-скрипте). Для некоторых консольных программ сделал примитивный аналог DejaGnu: простенький файл входа/выхода, запуск под valgrind, проверка, совпадают ли результаты... Пробовал DejaGnu, но как-то не пошло...
Здравствуйте, FrozenHeart, Вы писали:
FH>Скажите, пожалуйста, всегда ли вы пишете юнит-тесты? На каких языках вы программируете и какие библиотеки используете для их написания? Делаете 100% покрытие кода тестами или "забиваете" на юнит-тестирование определённых частей проекта?
Всегда. Ruby+RSpec.
FH>Я вот, например, до сих пор не могу понять, как можно писать тесты в некоторых случаях. Например, пишу я агрегатор объявлений с avito.ru на Python. Использую при этом фреймворк под названием Scrapy, который абстрагирует меня практически от всего (HTTP-реквесты, переподключения, редиректы etc). Всё, что мне остаётся -- вытащить из результата, переданного в мой callback в виде HTML-страницы, несколько значений, после чего записать их в БД / Excel-файл / etc. Что при таком подходе можно тестировать? Успешность выполнения GET-реквестов и валидность данных, которые прилетают мне в ответе? Так они выполняются где-то в недрах фреймворка, я даже знать-не знаю, какие там методы вызываются и что конкретно происходит. Даже если я посмотрю в исходники и увижу, что на самом нижнем слое находится, например, urllib из стандартной библиотеки Python, то полагаться на недокументированное поведение, на мой взгляд, в данном случае глупо. Успешность записи данных в БД / Excel-файл? Первое вообще непонятно, как зафейлиться может (разве что структура таблиц не совпадает с используемой мной моделью), второе ещё ладно.
Ну, это. Описываем что делает код русским языком, тестируем что он делает именно это.
В описанном случае, я бы сделал следующее:
1. заглушку на релевантные урлы, возвращающую заранее сохранённые данные (в руби есть для этого webmock, позволяющий поставить заглушку на «запросы вообще», вне зависимости от того какой конкретной библиотекой они выполняются; в питоне есть по-моему FakeWeb для этого)
* можно также использовать библиотеку автоматической записи реквестов (в руби есть vcr, в питоне наверняка есть аналог) — тогда время от времени стирая закешированные реквесты, можно убеждаться, что какой-нибудь мелкий редизайн авито не сломал код
2. дальше стандартное тестирование happy и unhappy pathes:
* когда я свою библиотеку вызываю для такого-то раздела, она выполняет запрос всех объявлений этого раздела
* когда я свою библиотеку вызываю для такого-то объявления, она выполняет разбор объявления и пишет его в БД
* когда я свою библиотеку вызываю для отсутствующего раздела, она делает что-то предсказуемое
* когда в объявлении не хватает нужных для моего кейса данных/объявление ненужное/что-там-у-вас-ещё-может-случиться — происходит что-то предсказуемое
....
Поскольку над проектом работаю один, от ЮТ отказался и пишу интеграционные(функциональные) тесты. FH>Привет всем!
FH>Я думаю, все здесь считают, что написание юнит-тестов -- это неотъемлимая часть разработки любого более-менее порядочного программного обеспечения, не так ли?
Код должен быть тестируем и тестирован, а уж как -- хозяйское дело.
FH>Скажите, пожалуйста, всегда ли вы пишете юнит-тесты? На каких языках вы программируете и какие библиотеки используете для их написания? Делаете 100% покрытие кода тестами или "забиваете" на юнит-тестирование определённых частей проекта?
C#. Написал свой опереточный фреймворк для прогона интеграционных тестов.
FH>Я вот, например, до сих пор не могу понять, как можно писать тесты в некоторых случаях. Например, пишу я агрегатор объявлений с avito.ru на Python. Использую при этом фреймворк под названием Scrapy, который абстрагирует меня практически от всего (HTTP-реквесты, переподключения, редиректы etc). Всё, что мне остаётся -- вытащить из результата, переданного в мой callback в виде HTML-страницы, несколько значений, после чего записать их в БД / Excel-файл / etc. Что при таком подходе можно тестировать? Успешность выполнения GET-реквестов и валидность данных, которые прилетают мне в ответе? Так они выполняются где-то в недрах фреймворка, я даже знать-не знаю, какие там методы вызываются и что конкретно происходит. Даже если я посмотрю в исходники и увижу, что на самом нижнем слое находится, например, urllib из стандартной библиотеки Python, то полагаться на недокументированное поведение, на мой взгляд, в данном случае глупо. Успешность записи данных в БД / Excel-файл? Первое вообще непонятно, как зафейлиться может (разве что структура таблиц не совпадает с используемой мной моделью), второе ещё ладно.
Перед запускам каждого теста (или всех сразу) поднимать какой-то сервис глушилку, который бы отдавал заранее известные данные, проверять что ответ соотв. ожидаемому, сохранять в бд, проверить, что все сохранилось нормально. Что касается сторонних библиотек, то тут ничего не попишешь: если у них баг, то репортите им, если очень срочно, то исправляйте сами.
S>Перед запускам каждого теста (или всех сразу) поднимать какой-то сервис глушилку, который бы отдавал заранее известные данные, проверять что ответ соотв. ожидаемому, сохранять в бд, проверить, что все сохранилось нормально
Т.е. подсовывать заранее заготовленную HTML-страницу? Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Зё>1. заглушку на релевантные урлы, возвращающую заранее сохранённые данные (в руби есть для этого webmock, позволяющий поставить заглушку на «запросы вообще», вне зависимости от того какой конкретной библиотекой они выполняются; в питоне есть по-моему FakeWeb для этого)
Тот же самый вопрос, что я задал в соседней ветке.
Т.е. подсовывать заранее заготовленную HTML-страницу? Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Зё>* можно также использовать библиотеку автоматической записи реквестов (в руби есть vcr, в питоне наверняка есть аналог) — тогда время от времени стирая закешированные реквесты, можно убеждаться, что какой-нибудь мелкий редизайн авито не сломал код
Если я правильно понял, как это работает, то мне всё равно в тестах надо будет указать реальный адрес страниц, на которые я буду выполнять реквесты, верно? Если так, то с avito такая тема не прокатит -- объявления не живут вечно, URL'ы в тестах со временем станут неактуальны.
Здравствуйте, FrozenHeart, Вы писали:
S>>Перед запускам каждого теста (или всех сразу) поднимать какой-то сервис глушилку, который бы отдавал заранее известные данные, проверять что ответ соотв. ожидаемому, сохранять в бд, проверить, что все сохранилось нормально
FH>Т.е. подсовывать заранее заготовленную HTML-страницу? Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Да, заранее заготовленную HTML-страницу. В любом случае приложение упадет после изменения страницы. Тут надо либо постоянно подстраиваться, либо искать другие подходы и т.д. У avito должно же быть какое-нибудь REST API? А тесты на то и нужны, чтобы проверять код по заранее известным входным данным.
Здравствуйте, FrozenHeart, Вы писали:
FH>Т.е. подсовывать заранее заготовленную HTML-страницу? Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Тестирование GUI это отдельная песня, тут надо смотреть в направлении систем, наподобие Sikuli. Юнит-тесты для такого бесполезны.
Здравствуйте, FrozenHeart, Вы писали:
Зё>>1. заглушку на релевантные урлы, возвращающую заранее сохранённые данные (в руби есть для этого webmock, позволяющий поставить заглушку на «запросы вообще», вне зависимости от того какой конкретной библиотекой они выполняются; в питоне есть по-моему FakeWeb для этого)
FH>Тот же самый вопрос, что я задал в соседней ветке.
FH>Т.е. подсовывать заранее заготовленную HTML-страницу? Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Ну в зависимости от сайта, я же навскидку говорю. В принципе, тесты можно (и зачастую нужно) делить модульные и интеграционные, например.
В данном случае «модульный тест» работает на заглушке, интеграционный — на реальных данных, например.
Интеграционный гоняется реже/отдельно.
Сломался интеграционный с сигналом «вёрстка того» — переделал модульные на новых заглушках.
Зё>>* можно также использовать библиотеку автоматической записи реквестов (в руби есть vcr, в питоне наверняка есть аналог) — тогда время от времени стирая закешированные реквесты, можно убеждаться, что какой-нибудь мелкий редизайн авито не сломал код
FH>Если я правильно понял, как это работает, то мне всё равно в тестах надо будет указать реальный адрес страниц, на которые я буду выполнять реквесты, верно? Если так, то с avito такая тема не прокатит -- объявления не живут вечно, URL'ы в тестах со временем станут неактуальны.
Ну можно начинать с какого-то урла раздела, который присутствует всегда. И тесты формулировать как «найдено X объявлений, у каждого есть автор, дата, (что вам там ещё важно)».
Здравствуйте, FrozenHeart, Вы писали:
FH>Скажите, пожалуйста, всегда ли вы пишете юнит-тесты? На каких языках вы программируете и какие библиотеки используете для их написания? Делаете 100% покрытие кода тестами или "забиваете" на юнит-тестирование определённых частей проекта?
Важно определиться с тем какую проблему ты решаешь. Тесты это не улучшайзер, это проверка качества. Если вспомнить, что качество это степень соответствования требованиям, то сразу вопрос — какие требования ?
Вот например ты пилишь компонент, который кидает эвент ready когда его состояние из loading стало complete. Вопрос- как ты к этому пришел ?
Например было так — "мы решили что здесь будет эвент". В этом случае никаких тестов писать нет смысла.
А если ты взял какую формальную модель, спроектировал на ее основе решение, то, внезапно, есть смысл проверить, обладает ли твое решение требуемыми свойствами формальной модели
Естественно, проверяешь не все, а только ту часть, на которую полагается вызывающий код. А именно
1 поведение
2 вычисления
3 реакция — сигналы об изменениях состояния
4 инварианты
5 пред и пост условия
Если на ровном месте появились моки, это значит что вместо проектирования у тебя кейс "а мы решили", см выше
Моки в юнит-тестах появиться не могут, потому или тесты не те, или проблема непонятная, или дизайн не тот.
Теоретически, можно взять и изобрести произвольную структуру связей. Здесь ты должен быть крут аки господь бог, что бы исследовать решение на предмет выявления разных свойств.
Требования часто неявные. Например требуется реализовать интерфейс, а дальше хоть так, хоть иначе, хоть как угодно. Фокус в том что код должен быть изначально проектироваться как тестопригодный, если ты планируешь покрывать тестами. Самый лучший вариант это функции навроде sin cos tan — тоесть, без состояния. Изменения состояния нужно протаскивать отдельной бухгалтерией.
Скажем, если ты свел решение к автомату с магазином, то стоит тестировать не все, а только функцию переходов(поведение) и АПИ автомата. Тестировать все тотально смысла не имеет
Самый главный принцип — красный юнит тест должен однозначно указывать на источник ошибки. Не 'что то отвалилось' а 'метод сенд актора обнуляет бехевиор если была ошибка'
UPD: На примере автомата — тесты функции переходов если в лоб, то это довольно хрупкие тесты. Вместо таких тестов есть вариант — четкая формальная модель автомата. Дальше покрывать тестами само поведение, здесь автомат должен быть изолирован от БД и прочих вещей.
Если автомат сделан в духе 'большой свич' то имеет смысл выразить все фокусы с переходами в виде тестов. Они будут хрупкими, но будут четко показывать, какой переход некорректный.
Собтсвенно, тесты поведения так же будут хрупкими. Например если состояние было открыто или закрыто, а стало начинаем открывать и открытие завершено и начинаем закрывать- закрытие завершено, то все тесты придется выбросить.
Здравствуйте, Ikemefula, Вы писали:
I>Самый главный принцип — красный юнит тест должен однозначно указывать на источник ошибки. Не 'что то отвалилось' а 'метод сенд актора обнуляет бехевиор если была ошибка'
Нет, не должен.
Потому что это не только бесполезно — зная об ошибке в определённом сценарии ты её и так найдёшь, т.е. ценна именно информация "у нас что-то не так", всё остальное приложится само собой — но и просто вредно, т.к. для более лучшего указания на источник ошибки начинается тестирование геттеров-сеттеров, конструкторов типа { this.param1 = param1; this.param2 = param2; } и прочие невероятно полезеные практики, столь улучшающин проценты метрик и столь тешащие сердца менеджмента.
Здравствуйте, Yoriсk, Вы писали:
I>>Самый главный принцип — красный юнит тест должен однозначно указывать на источник ошибки. Не 'что то отвалилось' а 'метод сенд актора обнуляет бехевиор если была ошибка'
Y>Нет, не должен. Y>Потому что это не только бесполезно — зная об ошибке в определённом сценарии ты её и так найдёшь,
А временем на поиски можно пренебречь ? Вообще то юнит-тесты избавляют от дебага. Классика юнит-тестирования — код теста содержит минимум кода и один-два ассерта. Отсюда ясно, что ошибка видна практически сразу, без необходимости дебага и поисков.
>т.е. ценна именно информация "у нас что-то не так", всё остальное приложится само собой — но и просто вредно, т.к. для более лучшего указания на источник ошибки начинается тестирование геттеров-сеттеров, конструкторов типа { this.param1 = param1; this.param2 = param2; } и прочие невероятно полезеные практики, столь улучшающин проценты метрик и столь тешащие сердца менеджмента.
Наоборот, геттеры и сеттеры размывают профит из за того, что тестируется 'что унутре'. С конструкторами ровно то же — тестирование 'что унутре'. Такой подход при малейшем телодвижении поломает вообще все тесты. ТО есть, сразу всё становится красным.
Скажем, для получения информации качества 'что-то не так' обычно и тесты не нужны
Здравствуйте, Ikemefula, Вы писали:
I>>>Самый главный принцип — красный юнит тест должен однозначно указывать на источник ошибки. Не 'что то отвалилось' а 'метод сенд актора обнуляет бехевиор если была ошибка' Y>>Нет, не должен. Потому что это не только бесполезно — зная об ошибке в определённом сценарии ты её и так найдёшь, I>А временем на поиски можно пренебречь?
Да, можно. По сравнению с обкладыванием каждого геттера-сеттера тестами для точного указания это на уровне погрешности.
I>Вообще то юнит-тесты избавляют от дебага. Классика юнит-тестирования — код теста содержит минимум кода и один-два ассерта. Отсюда ясно, что ошибка видна практически сразу, без необходимости дебага и поисков.
Ну да. Для сферической программы с абсолютным бюджетом. Юнит-тесты со 100% покрытием, полностью избавляющие от дебага и поисков сродини Снежному Человеку: все об этом говорят но никто не видел.
I>Наоборот, геттеры и сеттеры размывают профит из за того, что тестируется 'что унутре'. С конструкторами ровно то же — тестирование 'что унутре'. Такой подход при малейшем телодвижении поломает вообще все тесты. ТО есть, сразу всё становится красным.
Юнит тест должен однозначно указывать на источник ошибки. Не 'что то отвалилось' а "геттер GetX() не работает". А то эта ошибка вылезет в другом месте и, о ужас!, всё, сразу всё становится красным.
А теперь оказалось, что "наоборот".
Здравствуйте, Yoriсk, Вы писали:
Y>>>Нет, не должен. Потому что это не только бесполезно — зная об ошибке в определённом сценарии ты её и так найдёшь, I>>А временем на поиски можно пренебречь?
Y>Да, можно.
Если так, то ты подтвердил именно то, о чем я сказал — проблема уже найдена тестом и именно поэтому дальше искать не нужно. А вариант 'чтото отвалилось' таким свойством не обладает.
I>>Вообще то юнит-тесты избавляют от дебага. Классика юнит-тестирования — код теста содержит минимум кода и один-два ассерта. Отсюда ясно, что ошибка видна практически сразу, без необходимости дебага и поисков.
Y>Ну да. Для сферической программы с абсолютным бюджетом. Юнит-тесты со 100% покрытием, полностью избавляющие от дебага и поисков сродини Снежному Человеку: все об этом говорят но никто не видел.
А при чем здесь 100% покрытие ? У меня ощущение что ты забыл или что такое юнит-тесты, или что речь про юнит-тесты.
I>>Наоборот, геттеры и сеттеры размывают профит из за того, что тестируется 'что унутре'. С конструкторами ровно то же — тестирование 'что унутре'. Такой подход при малейшем телодвижении поломает вообще все тесты. ТО есть, сразу всё становится красным.
Y>Юнит тест должен однозначно указывать на источник ошибки. Не 'что то отвалилось' а "геттер GetX() не работает". А то эта ошибка вылезет в другом месте и, о ужас!, всё, сразу всё становится красным. Y>А теперь оказалось, что "наоборот".
Именно. 'что то отвалилось' превратилось в 'искать не нужно'.
Здравствуйте, FrozenHeart, Вы писали:
S>>Перед запускам каждого теста (или всех сразу) поднимать какой-то сервис глушилку, который бы отдавал заранее известные данные, проверять что ответ соотв. ожидаемому, сохранять в бд, проверить, что все сохранилось нормально
FH>Т.е. подсовывать заранее заготовленную HTML-страницу? Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Да, заранее подготовленную HTML страницу. Когда верстка настоящего сайта поменяется — подложишь новую страницу и отладишься на юнит-тестах, что скорее всего будет проще, чем отлаживаться на настоящем сайте.
Особенно полезным это становится, если вывод сайта может быть не в виде 1 верстки, на набора разных (от количества результатов, например). Или, скажем, авито придумает вставлять в середину списка результатов какую-то свою контекстную рекламу или что-то подобное, но не всегда, а с вероятностью 5%. Как тебе проверить, что страницу с такой рекламой твой исправленный код корректно обрабатывает? Прогнать весь код 20 раз по живым результатам? Но это все равно не даст тебе уверенности, что этот шанс выпал.
Здравствуйте, FrozenHeart, Вы писали:
FH> Т.е. подсовывать заранее заготовленную HTML-страницу?
Про это уже написали. А я добавлю про это:
FH> Если да, то тесты пройдут успешно, если даже вёрстка настоящего сайта уже давно поменялась, что вызовет некорректную работу приложения уже в процессе работы.
Пишутся ещё тесты (но это уже интеграционные, а не юнит), которые запускаются по расписанию, скажем, ежедневно, и посылают заранее определённые запросы и проверяют, что ответы соответствуют ожиданиям. Такие тесты позволят заметить внезапные изменения 3-rd party систем. Мы называем такое "compatibility tests".
Здравствуйте, Ikemefula, Вы писали:
I>Вот например ты пилишь компонент, который кидает эвент ready когда его состояние из loading стало complete. Вопрос- как ты к этому пришел ? I>Например было так — "мы решили что здесь будет эвент". В этом случае никаких тестов писать нет смысла.
Почему? Или вам не важно, воплощено ваше решение в жизнь или нет, и насколько качественно воплощено? Не пострадало ли при этом другое поведение и функциональность?
I>А если ты взял какую формальную модель, спроектировал на ее основе решение, то, внезапно, есть смысл проверить, обладает ли твое решение требуемыми свойствами формальной модели
Формальность/неформальность имеет значение для автоматической верификации, а не для тестов. Тесты — это в первую очередь способ найти очевидные ошибки, а не гарантия соответствия модели формальным спецификациям.
I>Если на ровном месте появились моки, это значит что вместо проектирования у тебя кейс "а мы решили", см выше I>Моки в юнит-тестах появиться не могут, потому или тесты не те, или проблема непонятная, или дизайн не тот.
Как раз такие моки в юнит-тестах необходимы во всех случаях, когда данный юнит зависит от других юнитов. Юнит-тест он и называется так, потому что тестирует исключительно данный юнит, а реализация других юнитов не должна влиять на тест. Тест, тестирующий юнит вместе с его зависимостями, называется интеграционным, хотя и реализуется обычно при помощи тех же инструментов и библиотек.
I>Теоретически, можно взять и изобрести произвольную структуру связей. Здесь ты должен быть крут аки господь бог, что бы исследовать решение на предмет выявления разных свойств.
Выявление "разных свойств" — это опять не про тесты. Тесты — это банальная защита от дурака, выявление самых типичных ошибок. Успешность выполнения тестов ничего не гарантирует, а только уменьшает количество итераций в релизном цикле и дает некоторую увереность в рефакторинге, которую, впрочем, не нужно переоценивать.
Социализм — это власть трудящихся и централизованная плановая экономика.
Здравствуйте, LaPerouse, Вы писали:
I>>Вот например ты пилишь компонент, который кидает эвент ready когда его состояние из loading стало complete. Вопрос- как ты к этому пришел ? I>>Например было так — "мы решили что здесь будет эвент". В этом случае никаких тестов писать нет смысла.
LP>Почему? Или вам не важно, воплощено ваше решение в жизнь или нет, и насколько качественно воплощено? Не пострадало ли при этом другое поведение и функциональность?
Качество это степень соответствия требованиям. Нет требований — нет вопроса. "мы решили что здесь будет эвент" — это никакое не требование
I>>А если ты взял какую формальную модель, спроектировал на ее основе решение, то, внезапно, есть смысл проверить, обладает ли твое решение требуемыми свойствами формальной модели
LP>Формальность/неформальность имеет значение для автоматической верификации, а не для тестов. Тесты — это в первую очередь способ найти очевидные ошибки, а не гарантия соответствия модели формальным спецификациям.
На примере LIFO — свойство такого контейнера в том, что на вершине всегда последний элемент. Это не может зависеть от реализации. И тестами ты проверяешь не ошибки, а вот такое вот соответствие модели.
Т.е.
var stack = new Stack();
stack.push(x);
assert(stack.peek() == x);
assert(stack.pop() == x);
То есть, тесты никакие ошибки не ищут. Ищет ошибки человек, проверяя различные свойства. Просто фокус в том, что нарушение вот таких вот свойств как правило и дает ошибки.
LP>Как раз такие моки в юнит-тестах необходимы во всех случаях, когда данный юнит зависит от других юнитов.
Покажи пример, где, по твоему, в юнит-тесте нужен мок. А я покажу тебе кое что взамен. Идёт ?
I>>Теоретически, можно взять и изобрести произвольную структуру связей. Здесь ты должен быть крут аки господь бог, что бы исследовать решение на предмет выявления разных свойств.
LP>Выявление "разных свойств" — это опять не про тесты.
Раузмеется. Тестами ты всего лишь покрываешь основные свойства. Если ты свойство не используешь ни явно, ни косвенно, то незачем и тест для него писать. Если в контейнере ты не используешь свойство length, то совершенно не важно, будут ли тесты этого свойства зелеными или красными. Их вообще быть не должно.
FH>Скажите, пожалуйста, всегда ли вы пишете юнит-тесты? На каких языках вы программируете и какие библиотеки используете для их написания? Делаете 100% покрытие кода тестами или "забиваете" на юнит-тестирование определённых частей проекта?
Тестировать надо с того что легко тестируется и возможно когда-то сломается.
То что ломалось и не было покрыто тестами, надо покрыть после правки, так чтобы потом оно гарантированно не повторилось.
После выполнения первых двух пунктов часто оказывается что сложных и нетестируемых мест не так уж и много осталось.
FH> Что при таком подходе можно тестировать? Успешность выполнения GET-реквестов и валидность данных, которые прилетают мне в ответе? Так они выполняются где-то в недрах фреймворка, я даже знать-не знаю, какие там методы вызываются и что конкретно происходит. Даже если я посмотрю в исходники и увижу, что на самом нижнем слое находится, например, urllib из стандартной библиотеки Python, то полагаться на недокументированное поведение, на мой взгляд, в данном случае глупо. Успешность записи данных в БД / Excel-файл? Первое вообще непонятно, как зафейлиться может (разве что структура таблиц не совпадает с используемой мной моделью), второе ещё ладно.
Делаешь тестовый набор данных которые получаешь на вход. Фиксируешь то что ожидаешь на выходе получить перед записью в базу.
Это покроет твой код, и в случае рефакторинга будет гарантировать что хотя бы основной функционал не сломался. А код внешних модулей это уже функциональное или интеграционное тестирование.
FH>В общем, поделитесь советами на тему того, что и как следует покрывать тестами, а что следует оставлять без покрытия. Чем вы руководствуетесь при написании юнит-тестов?
На что уходит больше всего времени при разборе полетов, то и покрывается в первую очередь.
Тогда и ты сам будешь видеть реальную пользу от тестов и вопросы приоритета отпадут. Зато встанут вопросы архитектуры.