Сообщение Re[20]: Что такое Dependency Rejection от 23.12.2023 14:26
Изменено 23.12.2023 14:30 Буравчик
Re[20]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:
Б>>Что значит дизайн такой? Это логика приложения такая, как ее по другому задизайнить?
P>То и значит — вы приняли решениче, что все будет в одной функции мутабельным вперемешку с тяжелыми зависимостями.это и есть ваш дизайн
Ну, у тебя (будем на ты?) то же самое написано. У тебя тяжелые зависимости вынесены в функции, а у меня вынесены в отдельные классы, закрыты интерфейсами (они в дальнейшем и мокаются), передаются как параметры.
Моки позволяют отрезать эти тяжелые вычисления и заменить их на простое поведение для теста.
P>Похоже, что моки про которые вы говорите, это фаулеровские стабы
Стабы/моки — вопрос терминологии. Я называю моками все разновидности моков, а стабами те, которые работают как "функция" — получили результат, обработали, вернули нужны.
Используются в основном стабы. Они просто заменяют в рамках теста тяжелое поведение на что-то легкое.
Моки, для которых проверяется факт вызова "с параметров а-б-в", тоже изредка могут применяться в основном для проверки отправки сообщений, где ответ не важен (в кафку, аудит).
P>Варианты подходов к тестированию, я их вам показал. Ветвления влияют на количество тестов, но не на подход к тестированию
P>А вот конкретное оформление этого ветвления определяет то, какими могут быть тесты
Про дизайн. Подход с моками как раз не навязывает дизайн (не вынуждают выделять функции).
Где уместно, я создам чистую функцию и тесты (обычно для алгоритмов бизнес-логики). Где не уместно — буду использовать моки (обычно для логики приложения).
Т.е. любую сложную логику при желании могу протестить юнит-тестами
P>>>Проблема в самих зависимостях, их много, и в том, что ваш дизайн мутабельный
P>>>Попробуйте отказаться от мутабельного дизайна. Как минимум, три эффекта из четырех легко отделяются. Так что можно упростить до безобразия.
Они у меня эти зависимости отделены. Выделены в отдельные классы (репозитории и т.п.), прокидываются как параметры.
P>Потом преобразуете метод в класс, v1,v2,v3 уходят в члены класса
P>
P>Теперь в тестах вы можете создать n троек значений, m результатов операций
P>И покроется все одной таблицей тестов n x m
Как тест будет написан? Как подготовлен, как будут передаваться эти n x m, как проверен?
P>Любители моков правда очень обрадуются, умножат(sic!) Количество тестов за счет проверок "commit вызывается после двух load"
Это твое (неправильное) понимание
P>Теперь ваш мок repo.get должен считать вызовы — что его вызвали первый раз с v1, второй раз с v2, и это важно, т.к на втором разе вы должны вернуть кое что другое
P>И это именно то, чего делать не стоит — в сложной логике ваш мок репозитория будет чудовищем, которое мало будет походить на реальную часть системы
P>Чем более унивесальная зависимость, тем труднее её мокать, и тем выше шансы на ошибку
Для этого теста (который будет проверять эту ветку логики) будет написан такой стаб:
P>Если не хотите возиться с интерфейсом — можно вытащить repo.get и подобное в методы рядом с do_logic. Идея на самом деле так себе, этот подход стоит применять если у вас не сильно много зависимостей
P>
P>Выглядит компактно — только штука в том, что вам придется мокать сам класс, ну хотя бы через наследника под тесты или сетать все методы и надеятся что ничего не пропустите.
Да, выглядит компактно и понятно. И никто не заставляет наворачивать дизайн как у тебя — с дополнительными функциями, от которых код становится сложнее.
Да, часть сложности перекладывается на тесты, из-за моков, но не критично.
P>А вот зависимости как параметры вы можете понасоздвать или нагенерить на все случаи жизни
P>1 ошибки всех сортов
P>2 пустой результат
P>3 куча вариантов ответа
P>4 куча сочетаний того и этого
Зависимости отделены также, как и у тебя. В твоем случае в тесты ты передаешь их как параметры-функции, а я подменяю поведение через стаб (по-сути ту же функцию). И в обоих случаях проверяются все варианты
Б>>Что значит дизайн такой? Это логика приложения такая, как ее по другому задизайнить?
P>То и значит — вы приняли решениче, что все будет в одной функции мутабельным вперемешку с тяжелыми зависимостями.это и есть ваш дизайн
Ну, у тебя (будем на ты?) то же самое написано. У тебя тяжелые зависимости вынесены в функции, а у меня вынесены в отдельные классы, закрыты интерфейсами (они в дальнейшем и мокаются), передаются как параметры.
Моки позволяют отрезать эти тяжелые вычисления и заменить их на простое поведение для теста.
P>Похоже, что моки про которые вы говорите, это фаулеровские стабы
Стабы/моки — вопрос терминологии. Я называю моками все разновидности моков, а стабами те, которые работают как "функция" — получили результат, обработали, вернули нужны.
Используются в основном стабы. Они просто заменяют в рамках теста тяжелое поведение на что-то легкое.
Моки, для которых проверяется факт вызова "с параметров а-б-в", тоже изредка могут применяться в основном для проверки отправки сообщений, где ответ не важен (в кафку, аудит).
P>Варианты подходов к тестированию, я их вам показал. Ветвления влияют на количество тестов, но не на подход к тестированию
P>А вот конкретное оформление этого ветвления определяет то, какими могут быть тесты
Про дизайн. Подход с моками как раз не навязывает дизайн (не вынуждают выделять функции).
Где уместно, я создам чистую функцию и тесты (обычно для алгоритмов бизнес-логики). Где не уместно — буду использовать моки (обычно для логики приложения).
Т.е. любую сложную логику при желании могу протестить юнит-тестами
P>>>Проблема в самих зависимостях, их много, и в том, что ваш дизайн мутабельный
P>>>Попробуйте отказаться от мутабельного дизайна. Как минимум, три эффекта из четырех легко отделяются. Так что можно упростить до безобразия.
Они у меня эти зависимости отделены. Выделены в отдельные классы (репозитории и т.п.), прокидываются как параметры.
P>Потом преобразуете метод в класс, v1,v2,v3 уходят в члены класса
P>
P>class Logic(v1, v2, v3): // параметры-значения в конструкторе
P> def run(load, load2, commit): // эффекты-зависимости здесь, эдакий аналог импорта
P> p = ctx.load(self.v1)
P> if что-то(p):
P> p2 = ctx.load2(self.v2) // load2 - абстракция так себе, должно быть хорошее имя
P> p = update(p, p2)
P> if что-то(p):
P> ctx.commit(self.v3)
P> return p
P>
P>Теперь в тестах вы можете создать n троек значений, m результатов операций
P>И покроется все одной таблицей тестов n x m
Как тест будет написан? Как подготовлен, как будут передаваться эти n x m, как проверен?
P>Любители моков правда очень обрадуются, умножат(sic!) Количество тестов за счет проверок "commit вызывается после двух load"
Это твое (неправильное) понимание
P>Теперь ваш мок repo.get должен считать вызовы — что его вызвали первый раз с v1, второй раз с v2, и это важно, т.к на втором разе вы должны вернуть кое что другое
P>И это именно то, чего делать не стоит — в сложной логике ваш мок репозитория будет чудовищем, которое мало будет походить на реальную часть системы
P>Чем более унивесальная зависимость, тем труднее её мокать, и тем выше шансы на ошибку
Для этого теста (который будет проверять эту ветку логики) будет написан такой стаб:
if v1:
return value1
else if v2:
return value2
else:
raise
P>Если не хотите возиться с интерфейсом — можно вытащить repo.get и подобное в методы рядом с do_logic. Идея на самом деле так себе, этот подход стоит применять если у вас не сильно много зависимостей
P>
P>do_logic(v1, v2, v3):
P> p = self.load(v1)
P> if что-то(p):
P> p2 = self.load2(v2)
P> p = update(p, p2)
P> if что-то(p):
P> self.commit(v3)
P> result = serialize(p)
P> return result
P>
P>Выглядит компактно — только штука в том, что вам придется мокать сам класс, ну хотя бы через наследника под тесты или сетать все методы и надеятся что ничего не пропустите.
Да, выглядит компактно и понятно. И никто не заставляет наворачивать дизайн как у тебя — с дополнительными функциями, от которых код становится сложнее.
Да, часть сложности перекладывается на тесты, из-за моков, но не критично.
P>А вот зависимости как параметры вы можете понасоздвать или нагенерить на все случаи жизни
P>1 ошибки всех сортов
P>2 пустой результат
P>3 куча вариантов ответа
P>4 куча сочетаний того и этого
Зависимости отделены также, как и у тебя. В твоем случае в тесты ты передаешь их как параметры-функции, а я подменяю поведение через стаб (по-сути ту же функцию). И в обоих случаях проверяются все варианты
Re[20]: Что такое Dependency Rejection
Здравствуйте, Pauel, Вы писали:
Б>>Что значит дизайн такой? Это логика приложения такая, как ее по другому задизайнить?
P>То и значит — вы приняли решениче, что все будет в одной функции мутабельным вперемешку с тяжелыми зависимостями.это и есть ваш дизайн
Ну, у тебя (будем на ты?) то же самое написано. У тебя тяжелые зависимости вынесены в функции, а у меня вынесены в отдельные классы, закрыты интерфейсами (они в дальнейшем и мокаются), передаются как параметры.
Моки позволяют отрезать эти тяжелые вычисления и заменить их на простое поведение для теста.
P>Похоже, что моки про которые вы говорите, это фаулеровские стабы
Стабы/моки — вопрос терминологии. Я называю моками все разновидности моков, а стабами те, которые работают как "функция" — получили результат, обработали, вернули нужны.
Используются в основном стабы. Они просто заменяют в рамках теста тяжелое поведение на что-то легкое.
Моки, для которых проверяется факт вызова "с параметров а-б-в", тоже изредка могут применяться в основном для проверки отправки сообщений, где ответ не важен (в кафку, аудит).
P>Варианты подходов к тестированию, я их вам показал. Ветвления влияют на количество тестов, но не на подход к тестированию
P>А вот конкретное оформление этого ветвления определяет то, какими могут быть тесты
Про дизайн. Подход с моками как раз не навязывает дизайн (не вынуждают выделять функции).
Где уместно, я создам чистую функцию и тесты (обычно для алгоритмов бизнес-логики). Где не уместно — буду использовать моки (обычно для логики приложения).
Т.е. любую сложную логику при желании могу протестить юнит-тестами
P>>>Проблема в самих зависимостях, их много, и в том, что ваш дизайн мутабельный
P>>>Попробуйте отказаться от мутабельного дизайна. Как минимум, три эффекта из четырех легко отделяются. Так что можно упростить до безобразия.
Они у меня эти зависимости отделены. Выделены в отдельные классы (репозитории и т.п.), прокидываются как параметры.
P>Потом преобразуете метод в класс, v1,v2,v3 уходят в члены класса
P>
P>Теперь в тестах вы можете создать n троек значений, m результатов операций
P>И покроется все одной таблицей тестов n x m
Как тест будет написан? Как подготовлен, как будут передаваться эти n x m, как проверен?
P>Любители моков правда очень обрадуются, умножат(sic!) Количество тестов за счет проверок "commit вызывается после двух load"
Это твое (неправильное) понимание
P>Теперь ваш мок repo.get должен считать вызовы — что его вызвали первый раз с v1, второй раз с v2, и это важно, т.к на втором разе вы должны вернуть кое что другое
P>И это именно то, чего делать не стоит — в сложной логике ваш мок репозитория будет чудовищем, которое мало будет походить на реальную часть системы
P>Чем более унивесальная зависимость, тем труднее её мокать, и тем выше шансы на ошибку
Для этого теста (который будет проверять эту ветку логики) для операции repo.get будет написан такой стаб:
P>Если не хотите возиться с интерфейсом — можно вытащить repo.get и подобное в методы рядом с do_logic. Идея на самом деле так себе, этот подход стоит применять если у вас не сильно много зависимостей
P>
P>Выглядит компактно — только штука в том, что вам придется мокать сам класс, ну хотя бы через наследника под тесты или сетать все методы и надеятся что ничего не пропустите.
Да, выглядит компактно и понятно. И никто не заставляет наворачивать дизайн как у тебя — с дополнительными функциями, от которых код становится сложнее.
Да, часть сложности перекладывается на тесты, из-за моков, но не критично.
P>А вот зависимости как параметры вы можете понасоздвать или нагенерить на все случаи жизни
P>1 ошибки всех сортов
P>2 пустой результат
P>3 куча вариантов ответа
P>4 куча сочетаний того и этого
Зависимости отделены также, как и у тебя. В твоем случае в тесты ты передаешь их как параметры-функции, а я подменяю поведение через стаб (по-сути ту же функцию). И в обоих случаях проверяются все варианты
Б>>Что значит дизайн такой? Это логика приложения такая, как ее по другому задизайнить?
P>То и значит — вы приняли решениче, что все будет в одной функции мутабельным вперемешку с тяжелыми зависимостями.это и есть ваш дизайн
Ну, у тебя (будем на ты?) то же самое написано. У тебя тяжелые зависимости вынесены в функции, а у меня вынесены в отдельные классы, закрыты интерфейсами (они в дальнейшем и мокаются), передаются как параметры.
Моки позволяют отрезать эти тяжелые вычисления и заменить их на простое поведение для теста.
P>Похоже, что моки про которые вы говорите, это фаулеровские стабы
Стабы/моки — вопрос терминологии. Я называю моками все разновидности моков, а стабами те, которые работают как "функция" — получили результат, обработали, вернули нужны.
Используются в основном стабы. Они просто заменяют в рамках теста тяжелое поведение на что-то легкое.
Моки, для которых проверяется факт вызова "с параметров а-б-в", тоже изредка могут применяться в основном для проверки отправки сообщений, где ответ не важен (в кафку, аудит).
P>Варианты подходов к тестированию, я их вам показал. Ветвления влияют на количество тестов, но не на подход к тестированию
P>А вот конкретное оформление этого ветвления определяет то, какими могут быть тесты
Про дизайн. Подход с моками как раз не навязывает дизайн (не вынуждают выделять функции).
Где уместно, я создам чистую функцию и тесты (обычно для алгоритмов бизнес-логики). Где не уместно — буду использовать моки (обычно для логики приложения).
Т.е. любую сложную логику при желании могу протестить юнит-тестами
P>>>Проблема в самих зависимостях, их много, и в том, что ваш дизайн мутабельный
P>>>Попробуйте отказаться от мутабельного дизайна. Как минимум, три эффекта из четырех легко отделяются. Так что можно упростить до безобразия.
Они у меня эти зависимости отделены. Выделены в отдельные классы (репозитории и т.п.), прокидываются как параметры.
P>Потом преобразуете метод в класс, v1,v2,v3 уходят в члены класса
P>
P>class Logic(v1, v2, v3): // параметры-значения в конструкторе
P> def run(load, load2, commit): // эффекты-зависимости здесь, эдакий аналог импорта
P> p = ctx.load(self.v1)
P> if что-то(p):
P> p2 = ctx.load2(self.v2) // load2 - абстракция так себе, должно быть хорошее имя
P> p = update(p, p2)
P> if что-то(p):
P> ctx.commit(self.v3)
P> return p
P>
P>Теперь в тестах вы можете создать n троек значений, m результатов операций
P>И покроется все одной таблицей тестов n x m
Как тест будет написан? Как подготовлен, как будут передаваться эти n x m, как проверен?
P>Любители моков правда очень обрадуются, умножат(sic!) Количество тестов за счет проверок "commit вызывается после двух load"
Это твое (неправильное) понимание
P>Теперь ваш мок repo.get должен считать вызовы — что его вызвали первый раз с v1, второй раз с v2, и это важно, т.к на втором разе вы должны вернуть кое что другое
P>И это именно то, чего делать не стоит — в сложной логике ваш мок репозитория будет чудовищем, которое мало будет походить на реальную часть системы
P>Чем более унивесальная зависимость, тем труднее её мокать, и тем выше шансы на ошибку
Для этого теста (который будет проверять эту ветку логики) для операции repo.get будет написан такой стаб:
if v1:
return value1
else if v2:
return value2
else:
raise
P>Если не хотите возиться с интерфейсом — можно вытащить repo.get и подобное в методы рядом с do_logic. Идея на самом деле так себе, этот подход стоит применять если у вас не сильно много зависимостей
P>
P>do_logic(v1, v2, v3):
P> p = self.load(v1)
P> if что-то(p):
P> p2 = self.load2(v2)
P> p = update(p, p2)
P> if что-то(p):
P> self.commit(v3)
P> result = serialize(p)
P> return result
P>
P>Выглядит компактно — только штука в том, что вам придется мокать сам класс, ну хотя бы через наследника под тесты или сетать все методы и надеятся что ничего не пропустите.
Да, выглядит компактно и понятно. И никто не заставляет наворачивать дизайн как у тебя — с дополнительными функциями, от которых код становится сложнее.
Да, часть сложности перекладывается на тесты, из-за моков, но не критично.
P>А вот зависимости как параметры вы можете понасоздвать или нагенерить на все случаи жизни
P>1 ошибки всех сортов
P>2 пустой результат
P>3 куча вариантов ответа
P>4 куча сочетаний того и этого
Зависимости отделены также, как и у тебя. В твоем случае в тесты ты передаешь их как параметры-функции, а я подменяю поведение через стаб (по-сути ту же функцию). И в обоих случаях проверяются все варианты