Здравствуйте, ·, Вы писали:
P>>Я ж сказал — если я поменяю внутреннюю реализацию, никакие тесты падать не должны.
·>Т.е. если ты поменяешь 'select * from users where id=?' на 'select id, name, email from users where id=?' у тебя тесты и посыпятся.
Посыплются. Есть такой недостаток. Потому нужно всегда писать осмысленные вещи,ш а не затыкать хренью. select * появится в том случае, если некто вместо построения запроса так и захардкодит.
P>>Мы покрываем ожидания вызывающей стороны, а у неё как раз нету ожиданий "вызовем crypto hmac вот таким макаром"
·>Я уже показал как пишутся тесты с моками для hmac. Там не должно быть такого.
Я шота не могу найти. Не могли бы вы ссылкой поделиться?
P>>А у вас видение "раз hmac в требованиях, то нужны моки"
·>Ты врёшь.
Очевидно, это я так вас понял. Вы слишком много таких аргументов тащите
P>>Разумеется. Зачем продолжать мусолить заведомо однобокую статью?
·>Ок. Наконец-то ты согласился, что таки да, у тебя прод-кода больше. Нам такого не надо.
Я согласился с тем, что статья кривая, а вы читаете что у меня прод-кода больше. Вы там что курите?
P>>Он будет везде использоваться. А раз так, то ваш мок надо или посать под каждый тест. Или запилить в ём поддержку всех кейсов.
·>Нет, только те тесты, в которых ассертится логика, зависящая от текущего времени. В остальных может чего-нибудь дефолтное возвращаться.
Я примерно так и имел ввиду.
P>>А нам не надо везде тащить этот hmac и тестировать это в каждом тесте. юнит-тестами покрываем вычисление hmac по конкретному контенту, и всё. Остальное — в интеграционных, коих на порядок меньше чем юнит-тестов.
·>Что значит не надо? У тебя там deep equals и сравнение логов. А интеграционных у тебя тоже дофига, т.к. они покрывают каждый do_logic.
У вас тут мешанина — интеграционных тестов по количеству методов в апи, + сценарии.
Сравнение логов только там, где вы это используете.
А чем deep equals не угодил я без понятия
P>>Вопрос — надо ли их фиксить, если поменялась сигнатура getAll? Если нет — покажите это чудо примером
·>Это типичный рефакторинг add-remove method parameter. Если добавляем парам, то тот прод-код, который мы меняем, ведь теперь надо передавать какое-то значение в такой параметр — надо и в тесте зафиксировать изменение в поведении. Удаление парама — так вообще тривиально, IDE автоматом везде подчистит, тест-не тест, не важно.
Не совсем понятно, почему этот типичный рефакторинг должен ломать тесты. Вы в этом видите какуюто пользу, но никак не расскажете, что за она
·>Ну ты там ю-тест упоминал "а что если в колонке пусто". Вот такой соответствующий и-тест тоже придётся писать.
Непонятно, что за проблему хотите решить этим дополнительным тестом. Перепроверить, что юнит-тест таки валидный?
·>По сути это не столь важно. Если у тебя в бл будет nextFriday(now), то во всех контроллерах где зовётся этот метод придётся передавать туда это самое now. И придётся писать полные интеграционные тесты, что туда передаётся именно серверное текущее время, а не что-то другое.
·>Т.е. вместо одного легковесного ю-теста с моком для данного конкретного метода бл, нам теперь приходится писать туеву хучу тяжёлых и-тестов на каждый вызов этого метода из разных контроллеров. Ещё интересно послушать как именно ты собираешься в "curl" и-тестах ассертить, что туда передаётся именно текущее время.
Количество тестов будет одинаковым. Только вам нужно будет везде указывать свой мок. Вы почему то верите, что моки избавляют от интеграционных. Это разные вещи. Интеграционный тест проверяет 1) что сама склейка валидная, а не 2) способность к ней. Моки проверяют второе, а нам надо — первое.
·>У тебя появляются "дешевые", но бесполезные ю-тесты, т.к. и-тесты всё равно нужны, практически в том же количестве.
В том то и дело, что тесты как раз полезные. А количество интеграционных зависит от апи и сценариев.
·>Ну я не знаю что именно показать. Ну допустим хотим nextFriday() потестировать:
Вот и сравните разницу
var testSubject = new CalendarLogic();
var now = Samples.nextFriday.case1.in;
assertThat(testSubject.nextFriday(now)).equalTo(Samples.nextFriday.result);
В вашем случае точно так же нужно предусмотреть все кейсы, только у вас будет везде на 5 строчек больше
·>Я вполне допускаю, что внутри CalendarLogic отрефакторить nextFriday() так, чтобы оно звало какой-нибудь static nextFriday(now) который можно потестирвать как пюрешку, но зачем? Если у нас конечная цель иметь именно nextFriday(), то рисовать ещё один слой, увеличивать количество прод-кода ради "единственно верного дизайна" — только энтропию увеличивать.
Ну да, вы только и думаете в терминах моков
P>>·>Какой код будет в тесте для проверки этого?
P>>Вот, что нового вы здесь открыли для себя? const pattern = ?
·>Появилась возможность пальчиком тыкнуть в то, что у тебя в коде теста полный кусок текста запроса 'select * from users where id=?', а не какой-то магический паттерн, проверяющий что запрос "тот". Т.е. по большому счёту ты никак не можешь знать что запрос хотя бы синтаксически корректен. Пишутся такие тесты обычно копипастой текста запроса из прод-кода в тест-код и тестируют "как написано", а не ожидания. И валятся только после полной сборки и запуска всего приложения, нередко только в проде.
Расскажите, как вы вашими косвенными тестами дадите гарантию, что на проде никогда не случится ситуация, что запрос выродится в select * from users и у нас будут сотни тысяч записей вгружаться разом.
Подробнее пожалуйста.
Это вероятно вы тестируете чтото копируя чтото с прода. А я тестирую построение запроса, например, те самые фильтры. И логики в методе довольно много, т.к. в зависимости от разных фильтров у нас могут быть разные вещи.
P>>P>> in: [fn1],
P>> out: [fn2]
P>>expect(buildRequest(params)).to.deep.eq(pattern)
P>>
·>Что такое "fn1" и "fn2"? Как deep.eq будет это сравнивать?
По ссылке.
P>>Ну вот выполняете запрос на проде, прод падает. Ваши действия?
·>Воспроизвожу сценарий в тесте и фиксю код.
Непросто код а в т.ч. и построение запроса к орм или бд.
P>>Для этого нужно знать, какие запросы кому вы отправляете. ORM — пишите тест на построение запроса к ORM. Если же ORM такого не умеет — вот он случай для косвенных проверок, и для моков.
·>Как это можно знать, что запрос "тот"? Единственный вменяемый вариант — выполнить запрос и поглядеть что вернулось.
Что вы так думаете — это я помню. Вы никак не расскажете, как побороть data complexity, т.е. как убедиться что ваш запрос не выдаст какой нибудь вырожденный кейс и вгрузит всё.
P>>Смотря сколько фильтров и значений. Если у вас один филд и один простой фильтр <> — хватит десятка.
·>Не понял. Достаточно иметь одно значение и слать разные фильтры.
·>·>testSubject.save(new User(id, "Vasya")));
·>assertThat(testSubject.find(new Filter.byEqual("Vasya"))).isPresent();
·>assertThat(testSubject.find(new Filter.byNotEqual("Vasya"))).isEmpty();
·>assertThat(testSubject.find(new Filter.byEqual("Vasy"))).isEmpty();
·>assertThat(testSubject.find(new Filter.byLike("asy"))).isPresent();
·>...
·>
·>Ок... ну может для сортировки и пагинации может понадобиться чуть больше одного значения.
У вас здесь та самая data complexity. Вам нужно обосновать внятно, почему ваш фильтр никогда не вернет пустое выражение, и запрос не выродится в "вгрузить всё"
Кроме того, если фильтров много, то количество данных будет расти экспоненциально.
P>>Вы пока вообще ничего не продемонстрировали.
·>Я продемонстрировал как удалить код в твоём примере с hmac, например.
Я нашел только тот вариант, где вы мокаете hmac. У вас есть что лучше?
P>>Нам нужны гарантии, что этот запрос не повалит нам прод, когда в него пойдут произвольные комбинации фильтров и всего остального
·>И как ты эти гарантии собрался обеспечивать? Неиспользованием моков?
Здесь тесты не дают полного контроля над ситуацией. Нам нужно спроектировать запрос и доказать его корректность, а построение фильтров итд покрыть юнит-тестами.
P>>Функция может просто вернуть изменение состояния, и запись для аудита. А сохранять новое состояние и писать аудит вы будете снаружи. Но вы почему то такой вариант не рассматривает
·>Я уже рассказал. Это делает больше прод-кода, усложняет его и начинает требовать дополнительных тестов о том, что аудит таки пишется снаружи как надо куда надо.
Нет, не делает. И тестов становится меньше. Аудит пишется снаружи как надо — это в любом случае задача интеграционного теста, что у нас всё в сборе поддерживает аудит
P>>После сборки — тесты.
·>Т.е. сборка без тестов 1.5 часа, потом ещё тесты час?
Чото конкретно вас смущает?
P>>у вас вероятно только расширяется. Откройте любое взрослое апи — v1, v2, v3, deprecated итд.
·>v1,v2,v3 — это разные спеки, практически новое api.
Забавно, что ту часть, где deprecated вы скромно опустили. Вы так в слова играете?
P>>Вот аннотации и есть тот самый контракт — в нем указано всё, что надо. Кода еще нет, а интеграция уже готова и можно начинать одновременно писать и клиента, и тесты, и сам код бакенда.
·>Аннотации — часть кода. Если вы захотите переписать свой сервис на другой ЯП, ну там с js на rust — что вы будете делать со своими аннотациями?
Вы из какого века пишете? Если по аннотациям можно получить клиентский код, серверный, да еще и тесты с документацией, то почему нельзя для rust это проделать?
Смена стека технологий встречается куда реже, чем смена бд или изменения в апи.
Вы бы еще метеориты вспомнили