Форум
Архитектура программного обеспечения
Тема
Как правильно задавать вопросы
B
I
abc
U
X
3
X
3
H1
H2
H3
H4
H5
H6
Asm
C/C++
C#
Erlang
Haskell
IDL
Java
Lisp
MSIL
Nemerle
ObjC
OCaml
Pascal
Perl
PHP
Prolog
Python
Ruby
Rust
SQL
VB
Здравствуйте, Pauel, Вы писали: P>Здравствуйте, ·, Вы писали: P>>>·>Лучше код покажи. Так что-то не очень ясно. P>·>[code] P>>> expect(crypto.createHmac).toBeCalledWith('sha256', 'API_TOKEN'); P>·>[/code] P>·>Мде... Или это "особенности" js?.. Но допустим. Как ты это предлагаешь переделать с "value"? P>Например, нам нужно считать токен для стрима, нас интересует ид класса, ид сущность, версия сущность b секрет. P>[code] P>const stream = Stream.from({Sample.id, Sample.version, Sample.content}); P>expect(Token.from(stream, Sample.secret)).to.eq(Sample.sha256); P>[/code] P>Теперь вы можете покрыть все кейсы - 1-3 успешных, и ведёрко фейлов типа ид, секрет, версия не установлены. P>И теперь в приложении вам не надо мокать Token.from, т.к. вы знаете, что она соответствует нашим ожиданиям, и не надо проверять, что контроллер обновления стрима вызывает Token.from если ему необходимо отрендерить ETag. P>Все что вам необходимо - в интеграционны тестах вызвать тот самый контроллер и убедиться, что его значения совпадают с нашими. P>>>Ну да - проблемы нет а тесты стали красными. И так каждый раз. P>·>Ничего не понял. Если использование уязвимой тормозной либы - не проблема, то зачем вообще код трогать? P>ну вот представьте - либа слишком медленная. И вы находите ей замену. Идеальный результат - после замены тесты зеленые. P>А вы непонятно с какой целью хотите что бы тесты сломались. P>Что нового вы добавите в приложение починкой этих тестов? P>>>Иными словами я предлагаю использовать value тест где только можно, т.к. таких можно настрочить сколько угодно. P>·>Ок, покажи код как правильно должно быть по-твоему. P>Ниже будет пример подробный P>·>Теперь мы используем её в коде: P>·>[code] P>·>Result doSomething(Request r) { P>·> if(validateThing(r.part1)) { P>·> wroom(r.zcxv); P>·> if(validateThing(r.part2) { P>·> return NiceResult(r.a, r.b); P>·> } else { P>·> foBarBaz(); P>·> return SoSoResult(meh(r)); P>·> } P>·> } else { P>·> taramparam(r.aaa); P>·> return NoNoNo(zzzz(r)); P>·> } P>·>} P>·>[/code] P>·>Вот тут нам совершенно неважно что и как делает validateThing - тут нам надо всего три сценария рассмотреть: P>·>1. validateThing(part1) => returns false; P>·>2. validateThing(part1) => returns true; validateThing(part2) => returns false; P>·>3. validateThing(part1) => returns true; validateThing(part2) => returns true; P>·>вот это и будет замокано. P>Что я вам и говорю - вы сделали дизайн под моки :-) перемешивая изменения с вычислениями P>Вместо вот такого: P>[code] P> foBarBaz(); <- здесь у вас сайд эффекты P> return SoSoResult(meh(r)); P>[/code] P>у вас может быть следующее P>[code] P> const toDo = calculate(); // покрыто юнит тестами, т.к. простые вычисления без сайд эффектов P> return SoSoChanges(todo); P>[/code] P>а на вызывающей стороне вы вызываете какой нибудь [tt]result.applyTo(ctx)[/tt] P>Соответсвенно, все изменения у вас будут в конце, на самом верху, а до того - вычисления, которые легко тестируются. P>·>Создаётся мок для класса Thing. P>·>Дальше мокаешь методы и инжектишь в SUT. P>Вот-вот, еще чтото надо куда то инжектить, вызывать, проверять, а вызвалось ли замоканое нужное количество раз, в нужной последовательности, итд итд итд P>Итого - вы прибили тесты к реализации P>>>Вы там в своём уме? Чтобы замокать чего нибудь ради теста, вам нужно знание о реализации, следовательно, тесткейс автоматически становится вайтбоксом. P>·>В общем случае: чтобы передать что-то методу для теста, тоже надо знание о реализации, иначе не узнаешь corner-case. P>На мой взгляд это не нужно - самый минимум это ожидания вызывающей стороны. Всё остальное это если время некуда девать. P>>>Зачем? P>·>Я понимаю код лучше, чем твои объяснения. P>Я только-только поменял работу, к сожалению физически не могу показать типичный пример. P>>>В том то и проблема, что вы строите такой дизайн, что бы его потом моками тестировать. Потому и топите везде за моки. P>·>Хочу посмотреть на твой дизайн. P>Мой дизайн в основном в закрытых репозиториях. Пока что так. Опенсорсные проекты я только исследую или использую. P>·>Важно то, что в этом tryAcceptComposition находится connectionString. Т.е. условно говоря твой "интеграционнный тест" полезет в реальную субд и будет безбожно тормозить. P>Во первых - таких тестов у нас будет пару штук P>Во вторых - моки не избавляют от необхидимости интеграционных тестов, в т.ч. запуска на реальной субд >>В коде с моками - был reservationsRepository и возможность отмежеваться от реальной субд чтобы тестировать что параметры поиска и создания резерваций верные P>Вот я и говорю - вы сделали в бл зависимость от бд, а потом в тестах изолируетесь от нее. Можно ж и иначе пойти - изолироваться на этапе дизайна, тогда ничего мокать не надо P>>>Вы идете наоброт - выпихиваете всё в контроллер, тогда дешевыми юнит-тестами покрывать нечего, зато теперь надо контроллер обкладывать моками, чтобы хоть как то протестировать. P>·>В некоторых случаях интеграционным тестом будет "код скомпилировался". P>Если у вас много зависимостей - статическая типизация помогает слабовато. P>>>Если это не про моки, то вопросов еще больше. Каким образом иерархия контроллер <- сервис-бл <- репозиторий <- клиент-бд стала у вас вдруг плоской? P>·>DI же, не? P>·>[code] P>·>var db = new Db(connectionString); P>·>var repo = new Repo(db); P>·>var bl = new BlService(repo); P>·>var ctl = new Controller(bl); P>·>var server = new Server(port); P>·>server.handle("/path", ctl); P>·>server.start(); P>·>[/code] P>Я вижу, что здесь вы создаёте ту самую глубокую иерархию - у вас сервис bl зависит от репозитория который зависит от bd. P>Есть другой вариант - не вы передаете репозиторий в bl, а вызываете bl внутри т.н. use-case в терминологии clean architecture P>тогда получается иерархия плоская - у контроллера кучка зависимостей, но репозиторий не зависит от бд, бл не зависит от репозитория P>controller зависит от P>* db P>* repositotory P>* bl P>Вот наш юз кейс - интеграционный код, покрывается интеграционным тестом P>[code] P>const getUser = repository.getUser(id); // покрыто юнит тестом P>const oldUser = db.execute(getUser); P>const newUser = bl.modifyUser(oldUser); // покрыто юнит тестом P>const updateUser = repository.updateUser(oldUser, newUser); // покрыто юнит тестом P>const updatedUser = db.execute(updateUser); P>return updatedUser; P>[/code] P>вот наш контроллер - интеграционный код, покрывается интеграционным тестом P>[code] P>handler(id, ctx, metadata) { P> const useCase = UpdateUser.from(ctx); // покрыто юнит тестом P> const serializer = Serializer.from(ctx, metadata) // покрыто юнит тестом P> const result = userCase.invoke(id); P> return serializer.toResponse(result); // покрыто юнит тестом P>} P>[/code] P>>>Я думал вы собираетесь замокать все зависимости контроллера, но вы похоже бьете все рекорды :-) P>·>Для юнит-теста Controller надо мокать только BlService. P>вот это и проблема - у вас тест "как написано", т.к. далеко не факт, что ваш мок этого бл-сервиса соответствует реальному блсервису. Эта часть вообще никак не контролируется - и крайне трудно научить этому разработчикв. P>Типичная проблема - моки крайне слабо напоминают тот самый класс. P>>>Совсем необязательно. Тесты это далеко не единственный инструмент в обеспечении качества. Потому покрывать код идея изначально утопичная. Покрывать нужно требования, ожидания вызывающей стороны, а не код. P>·>Как? И как оценивать качество покрытия требований/ожиданий? P>А как это QA делают? Строчка требований - список тестов, где она фигурирует. P>>>Для самого кода у нас много разных инструментов - от генерации по мат-модели, всяких инвариантов-пред-пост-условий и до код-ревью. P>·>А метрики какие? Чтобы качество обеспечивать, его надо как-то измерять и контролировать. P>Строчки это плохие метрики, т.к. цикломатическая сложность может зашкаливать как угодно P>>>Да хоть вот так, утрирую конечно P>>>[code] P>>>curl -X POST -H "Content-Type: application/json" -d '{<резервирование>}' http://localhost:3000/api/v3/reservation P>>>[/code] P>·>А где ассерт? P>Это я так трохи пошутил - идея в том, что нам всего то нужен один тест верхнего уровня. он у нас тупой как доска - буквально как http реквест-респонс P>>>Постусловия к вам еще не завезли? Это блакбокс, только это совсем не тест. P>·>А куда их завезли? P>Много где завезли, в джава коде я такое много где видел. А еще есть сравнение логов - если у вас вдруг появится лишний аудит, он обязательно сломает выхлоп. Это тоже блекбокс P>·>Это слишком тяжелый тест. Количество таких тестов должно быть минимально. P>Разумеется - таких у нас по несколько штук на каждый роут P>>>Вы же моками обрезаете зависимости. Забыли? Каждая зависимость = причины для изменений. P>·>Я не понял на какой вопрос этот ответ. P>Вы спрашивали, почему короткая функция более устойчива по сравнению с maitred - потому, что у ней зависимостей меньше. Каждая зависимость, прямая или нет, это причина для изменения. P>Например, ваш бл-сервис неявно зависит от бд-клиента, через репозиторий. А это значит, смена бд сначала сломает ваш репозиторий, а это сломает ваш бл сервис. P>А раз вы его замокали для теста контроллера, то вам надо перебулшитить все тесты P>>>Вот с maitred все не просто - любое изменение дизайна затрагивает такой класс, тк у него куча зависимостей, каждая из которых может меняться по разными причнам. P>·>Куча это сколько? Судя по коду в статье я там аж целых одну насчитал. P>:-) прямых - одна, а непрямых - все дочерние от того же репозитория P>>>И вам каждый раз надо будет подфикшивать тесты. P>·>По-моему ты очень странно пишешь тесты и дизайнишь, от того и проблемы. P>Ничего странного - вы как то прошли мимо clean architecture. P>>>Тогда не совсем понятен ваш вопрос. P>·>https://en.wikipedia.org/wiki/Code_coverage P>Я не знаю, в чем вы проблему видите. В жээс аннотация это обычный код, который инструментируется, и будет светиться или красным, или зеленым P>>>·>Самое что ни на есть вайтбокс тестирование. Что конкретный метод содержит конкретную аннотацию. P>>>Мы тестируем, что есть те или иные метаданные. А вот чем вы их установите - вообще дело десятое. P>·>А как ещё на метод можно метаданные навесить? Или ты про js? P>разными способами P>1. аннотации метода P>2. аннотации класса P>3. аннотации контекста класса P>4. скриптованием, например, на старте P>5. конфигурация приложения P>6. переменные окружения P>>>А вот метаданные это совсем не вайтбокс, это нефункциональные требования. P>·>Зачем тогда их в эти тесты класть?.. P>затем и класть, что это часть требований. фиксируем капабилити P>>>Мне нужно будет добавить функцию которая будет возвращать экземпляр аудитора по метаданным и покрыть её простыми дешовыми тестами. А раз весь аудит проходить будет через неё, то достаточно сверху накинуть интеграционный тест, 1шт P>·>Тебе как-то рефлексивно придётся извлекать детали аудита из места вызова и параметров метода. На ЯП со статической типизацией будет полный швах. P>Зачем? Есть трассировка и правило - результат аудита можно сравнивать
Теги:
Введите теги разделенные пробелами. Обрамляйте в кавычки словосочетания с пробелами внутри, например:
"Visual Studio" .NET
Имя, пароль:
Загрузить
Нравится наш сайт?
Помогите его развитию!
Отключить смайлики
Получать ответы по e-mail
Проверить правописание
Параметры проверки …