Обновить
26
ApeCoder@ApeCoder

Разработчик

6
Подписчики
Отправить сообщение
Они просто перешли к вызывающему коду, теперь каждый кто вызывает printList должен указывать еще и эти [1..10] и printLn.

Вероятно, потому, что функция теперь называется неправильно. Она не печаетает а применяет действие к списку поэтому ее надо назвать по другому


В итоге уровень абстракции от такого преобразования на самом деле уменьшился, а не увеличился.

Функция скрывает, как именно происходит перебор — например значение i уже наружи не видно и не надо о нем заботиться


К примеру, для асинхронных операций код будет работать некорректно

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

Это if (x > 5) должно быть реализацией какого-то требования к тестируемому объекту. Соответственно если эти кейзы сформулированны в терминах требований, то не вижу проблемы в таком вайтбокс тестировании. Если же замокан модуль Math и Перегружена операция < и контроллирвется что она вызвана ровно один раз, то тест получится хрупким

Не является ли необходимая объекту среда частью контракта?

Интерфейс в с какой-то степенью задает поведение. Скорее всего, если у вас есть IStack то в комментарии к методу push будет написано, что значение может быть дальнейшем извлечено при помощи pop. То есть чтобы корректно реалзовать интерфейс надо соблюсти какие-то требования к поведению.


Зависимости могут быть частью интерфейса и тогда "моки" (в общем смысле — фейковые объекты) вполне себе помогают тестировать его как черный ящик.


Если тестировать не требования а реализацию, то тест не сможет ответить на вопрос, соответствует ли реализация требованиям

extends — то есть пишем предка. Отсюда и дерево мок-сервисов растёт

Принимается — интерфейсы представляют дерево => симуляторы представляют дерево.


зачем это?

Вы не используете front door — на front door написано "предоставьте мне IService и тогда я буду работать" а вы предоставляете подмножество IService.


Ваши тесты выражают не требования а реализацию — какие методы вызываются.


Дальше будут fragile overspecified tests.

Конечно. Вам надо писать файл. Перекладывать сообщения в нём. Писать методы получения параметров в нём. Возможно вам надоест писать однообразный код и вы начнёте наследоваться от базового файла ваших мок-текстов.

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


Кто-то начнёт «улучшать» ваши мок-файлы. Кто-то начнёт добавлять некую специфическую логику в эти уже общие мок-тесты. С благой целью, конечно.

Не будет — там должна быть одна логика — полная InMemory реализация интерфейса


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

Всё это чревато. Имхо.

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


Дерево файлов ваших мок-тестов начнёт расти.

Мок-реализаций. И это не дерево.


Я же решил, что тестируя метод, я пишу код, который зависит только и только от имплементации тестируемого метода

Use the front door first — не надо зависеть от реализации, тесты должны зависеть от требований. Почитайте Мезароса.


The types of interfaces we use has an influence on the robustness of our tests. The use of Back Door Manipulation (page X) to set up the fixture or verify the expected outcome or a test can result in Overcoupled Software (see Fragile Test on page X) that needs more frequent test maintenance. Overuse of Behavior Verification (page X) and Mock Objects (page X) can result in Overspecified Software (see Fragile Test) and tests that are more brittle and that may discourage developers from doing desirable refactorings.

Я просто настраиваю мок IService — то есть указываю, что при обращении к такому-то методу IService верни то-то и то-то.

Я тоже настраиваю C# своим файликом. Или вы считаете что int x(return y); чем-то отличается от x=>y?

Ну вот оно и быстро затухло

http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.html


I, for example, seldom use a mocking tool. When I need a mock (or, rather, a Test Double) I write it myself. It’s not very hard to write test doubles. My IDE helps me a lot with that. What’s more, writing the Test Double myself encourages me not to write tests with Test Doubles, unless it is really necessary. Instead of using Test Doubles, I back away a bit from micro-testing, and write tests that are a bit closer to functional tests. This too helps me to decouple the tests from the internals of the production code.
Нет, не так. Допустим обновили IService, добавив в него метод changeEmployeeProperty (метод changeName остался неизменным — его не меняли и не удаляли из IService ).
Это у вас упадут все тесты,

Не упадут. На этапе добавления метода IDE скажет, что он не реалтзован, и предложит сгенерировать заглушку с NotImplemented exception.


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


Потому, что фактически у вас метод просит IService, а вы ему передаете более ограниченный вариант сервиса (условно INameChanger), но называете его IService. Фактически у вас наружение LSP — из-за этого у вас тест тестирует реализацию (какиме именно сетоды сервиса вызывать — это интимные подробности реализации).


Когда я вижу развесистое дерево вручную написанных файлов для реализации различных мок-объектов

Так у вас он тоже вручную написан, только
1) Не до конца
2) Средствами Reflection — то есть IDE не будет поддерживать контроль целостности и удобство работы в той же мере, что и с классами написанными без Reflection

Да, в качестве небольшого количества тщательно подобранных тестов это можно использовать

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

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


Иначе при падении тестов будет непонятно, какое требование нарушилось

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

Это не трюк, это просто тест вам показал что покрытия недостаточно. Требует еще одного цикла Red-Green-Refactor. Правда автор начал со слишком сложного теста. Я бы сначала протестил самую простую ситуацию — такого просто нет в базе, а потом уже Василия.


В класических техниках TDD предлагается именно начинать с Василия.


Посмотрите например эту ката (RomainNumbers)

Он не говорил, что все надо заменить тестами, а только "где можно". Данный случай можно автоматически протестировать правилом "каверадж не должен падать"

Вы создаёте мок-сервис как полноценный класс (файл на диске) методы которого вы должны наверняка также тестировать — ибо ваш объект InMemoryService должен довольно таки хоть и «просто» но имитировать реальный сервис.

Так у вас создается тоже — только ограниченная абстракция. То есть ваш вариант не проверяет требование "если мне передали сервис, я изменю имя" а требование "если мне передали сервис, я вызову changeName" что является более узким требованиями.


То есть например, вдруг ваш сервис получит какой-то другой метод. например changeEmployeeProperty и для оптимизации надо будет использовать его — у вас упадут все тесты, которые использовали changeName, у меня надо будет только добваить метод в класс.


но вам всё равно придётся туда (в тестовые методы) посмотреть когда они станут красные

В том-то и дело, что они не станут красными, пока не будет изменение требований. Каким именно способом SUT добивается изменения имени — это не часть требований. То есть такие тесты будут давать много ложных срабатываний. Абстракция сервиса будет размазана по нескольким методам.


имплементировать сам сервис, создавая не такой уж простой файл мок-сервиса

Будет то же самое только размазанное по rкоду тестов


и, возможно (это зависит от сложности сервиса и желаемой надёжности кода этого мок-сервиса), писать тесты для методов этого «простого» мок-сервиса.

У вас тоже самое только вы никак не тестируете код мока. Есть большая разница, находится ли он внутри класса или размазан по методам класса?


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


Ваш тест фактически некорректен — в предусловии метода сказано "передайте мне IService", а вы ему передаете нечто похожее, только реализующее ровно один метод.

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

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


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

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


Какие книжки по тестированию вы читали?

Подводный камень — то, что получающйся тест плохой.


  • в коде теста все данные, не относящиеся к данному тестовому случаю дожны быть спрятаны
  • в тесте должно быть понятно, к какому кейзу это относится и почему так
  • входные данные в коде теста должны быть связаны с выходными

Если делать pinning test, то можно и таким пользоваться

Дык approval tests же. Примерно то же самое. Есть еще intellitest который геренирует входные данные

Это data driven tests и approval tests недостаток такой, что непонятно, из текста теста почему данные должны быть именно такими.

Информация

В рейтинге
Не участвует
Откуда
Россия
Дата рождения
Зарегистрирован
Активность