All streams
Search
Write a publication
Pull to refresh
@Druuread⁠-⁠only

User

Send message
> А протестировать при этом все еще можно?

А почему бы нельзя? Пишете тот же самый тест, что с моками, только без моков. В итоге затрат меньше (т.к. не надо тратить время на моки), а результат — лучше (т.к. поймано больше багов).
> Если у класса плохая тестируемость, это проблема не моков, а проектирования.

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

> В любом случае, если ваш тест проверяет более одного объекта — это НЕ юнит-тест по определению.

Тогда юнит-тесты в вашем определении — не нужны. Зачем использовать заведомо более плохой инструмент? В этом же нету смысла.
> Для сервисов наоборот, инфраструктурных вещей наоборот, лучше мочить.

Безусловно. Сложность настройки внешнего тестового окружения обычно не то что сравнима — превышает сложность настройки моков. А по-этому в данном случае моки вполне полезный инструмент — то есть это как раз тот случай, когда «не обойтись».
> Мнение, агрументированное… другие мнением.

Да все абсолютно, кто когда-либо писал юнит-тесты, встречались с адом, когда настройка моков занимает 3/4 самих тестов. При этом без моков этого кода бы просто не было.

> Между тем, о каком качестве юнит-тестов может идти речь, если тестируемый объект не будет изолирован от остального приложения?

Назначение тестов — поиск ошибок. Количество ошибок, которые отловил ваш тест, деленное на затраты для написания теста — это и есть прямая оценка качества данного теста.

Тесты с моками ловят строго меньше ошибок (так как снижают покрытие, для сравнимого покрытия тестов с моками требуется обычно в разы больше), при этом они ведут к более частому рефакторингу (а тест до и после рефакторинга — это разные тесты), и увеличивают затраты на написание теста (так как тесты становятся сложнее). Так что, да, повсеместное мокирование в итоге снижает качество тестов.
> 4. Чаще используйте заглушки (моки) вместо реальных объектов.

Весьма спорно. Есть мнение, что моки/стабы надо использовать только в тех случаях, когда без них не обойтись, так как они ведут к резкому росту сложности тестового кода и снижению качества самих тестов.
Почему-то во всех подобных статьях забывают про одну важную вещь — человек обычно приходит на собеседование не для того, чтобы рассказывать о себе, а для того, чтобы узнать подробности о месте работы, в первую очередь. И вопросы следует в основном задавать, а не отвечать (параллельно высказывая свое мнение об ответе).
> GraphQL — пожалуй первый более-менее дошедший до масс заход в эту тему, хотя проблем там тоже тьма.

Чем асинхронный запрос на сервер с GraphQL отличается от такого же запроса на сервер с классическим REST? С точки зрения фронтенда же разницы вообще нет.
Конечно, можно. Можно вообще реализовать интерпретатор саг в thenable и у вас будут те же самые саги, только вместо yield — await. И код, конечно, будет синхронным.
Вы уверены, что Mayorovp предполагал именно такую реализацию для call? Я более чем уверен в обратном. Прекратите путать людей.

Ну и, да, в обоих случаях код синхронный (при таком call). Что дальше? Что вы этим пытаетесь доказать? Как часто вы используете такой call на практике — и зачем?
Функция, конечно. Обычная, синхронная функция, которая синхронно возвращает обычный объект вроде { type: «CALL», payload: { func: Api.fetchUser, args: [ action.payload.userId ] } }. Кто и что потом будет делать с этим объектом — неизвестно, узнать из саги это никак нельзя.

В случае же с промисом у вас внутри ф-и call будет применение Api.fetchUser к аргументам — с-но, асинхронный вызов промиса.
У вас другой код. Зачем вы путаете людей?
> В каком таком «предоставленном коде» есть функция call?

> const user = await _call_(Api.fetchUser, action.payload.userId);
> Почему вы не рассматриваете возможность наличия заглушки на месте функции call?

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

> Но перестанет ли от этого код быть асинхронным?

В первом случае код _не асинхронный_. Вам никто не гарантирует, что там будет асинхронный вызов, это зависит не от приведенного кода, а от кода интерпретатора, на который данный код никак не ссылается.
В том, что во второй строчке происходит асинхронный вызов, а в первой строчке никакого асинхронного вызова не происходит, происходит вполне синхронный возврат call-объекта. Что с ним там будет дальше происходить — уже от самого кода саги не зависит. Интерпретатор саги может, например, просто посмотреть на него и всунуть обратно заглушку. При этом никто никуда не будет отправлять никаких запросов, а сага прекрасно будет исполняться в соответствии со своей синхронной, декларативной спецификацией.
> Если подумать — а откуда вообще асинхронщина в приложении? И почему она такая сложная.

В сагах нету асинхронного кода. Они полностью синхронные.
> А с сагами мы получаем два места, где можно писать код — саги, и редьюсеры.

Верно. Сами редьюсеры можно писать в сагах (и многие так делают). А редакс при наличии саг по факту не нужен, надеюсь, все-таки отвяжут саги от редакса.
> В случае saga, у тебя вся бизнес-логика переезжает в саги, а саги — это лапша асинхронной императивщины.

Саги — это _в точности_ эффекты из elm. Разницы две:
1. Эффекты в elm исполняет сам ранатйм elm в потрохах, а эффекты саг — исполняет саговский middleware.
2. js, в отличии от elm, благодаря синтаксису генераторов поддерживает синтаксис, аналогичный синтаксису do-нотации (для определенного подмножества монад). Именно по-этому саги выглядят как «императивная лапша» (то есть хорошо и удобно, на самом деле, выглядят). При этом весь код с сагами спокойно можно написать без генераторов вообще (только придется по-другому сделать middleware).
> В LINQ to Entities в качестве спецификаций используется Expression<Func<T, bool>>. Но такие выражения нельзя комбинировать с помощью булевских операторов и использовать в LINQ to Objects.
> Попробуем совместить оба подхода. Добавим метод ToExpression():

Какой в этом смысл? Не лучше ли просто определить методы расширения на Expression<Func<>>?
> Вы предпочитаете полотна?

При чем тут полотна? Естественно, код разделяется на компоненты. Но семантически, когда это нужно. А когда это не нужно (как в случае искусственного выделения условных веток) — не разделяется.

> Я ее не рассматриваю

То есть вы просто игнорируете неудобные для своей гипотезы факты?

> Ну так не надо говорить тогда, что это HOC. У вас банально функция будет принимать класс и возвращать класс.

Так в ангуляре классы компонентов ничем и не отличаются от обычных классов.

> И от обертки вы не избавились.

От обертки избавляться в данном случае не нужно, потому что ее не будет. откуда вообще вы ее выдумали?

> И пример не привели.

Я пример не привел потому, что вы изначально потребовали надуманные невыполнимые условия. Давайте я потребую привести от вас в реакте аналогичный HOC, который можно будет применять, не объявляя отдельную переменную для результирующего класса. Вы пример не приведете, потому что его написать невозможно, и я объявлю, что НОС в реакте не существует. Глупость же.

> повышение порядка — это повышение уровня абстракции и гибкости и как следствие снижение копипасты.

Это, конечно, далеко не всегда так.

> В каком месте HOC нарушает хоть что-то?

S, O, D — нарушается откровенно.

> Ну окей, если вам нравится такая модель общения — в реакте нет директив, потому-что они не нужны, так как есть HOC, выразительные, гибкие, понижающие связность кода, упрощающие его, убирающие копипасту и повышающие качество кода в целом. Как вам?

Это утверждение противоречит наблюдаемому факту — в ангуляре есть и директивы, и НОС, но при этом никто не использует НОС, когда можно использовать директивы. Из этого я делаю вполне очевидный вывод, что НОС в реакте используется только из-за того, что нету адекватных альтернатив. Как только в реакте добавят какой-то удобный инструмент вроде директив — НОС сразу начнут использовать гораздо реже.

> Нет. Мы рассуждаем о HOC в контексте реакта, так как в ангуляре их нет.

Они есть. Любая функция, которая принимает класс компоненты и возвращает другой класс компоненты — является НОС в ангуляре. Например, ф-я, которая подмешивает к компоненту «логику мигания». От того, что вы спрятались в домик и закрыли глаза — реальность не изменится.

> Но они ни являются компонентами, ни возвращают компоненты.

Они не являются компонентами в реакте, но являются компонентами в ангуляре. Понятие компонента в ангуляре и реакте — разное. В реакте не существует компонентов в понимании ангуляра (т.к. класс без логики рендера не является компонентом), а в ангуляре не существует компонентов в понимании реакта (т.к. компонент не может содержать логику рендера).
> Бьете на компоненты и собираете в композицию.

Ну так мы возвращаемся к началу — в случае с шаблонами не бью, а в случае с jsx — вынужден бить, когда длоя этого по факту нету никаких причин кроме «иначе код вырвиглазен». И это чрезмерное разделение приводит к негативному результату, значительно повышая сложность приложения.

> Аааааа, вы прикалываетесь? Это же корявое месиво из невнятных конструкций, сплошной синтаксический шум.

Синтаксис не существенен, это вопрос привычки. В пример можно привести известную картинку о лиспе и скобках. А вот структура кода — важна весьма.
И, насчет синтаксиса — будем честны, в JSX месива никак не меньше.

> Мне не интересно это мигание.

Но именно эту задачу мы и рассматриваем. Еще раз, по порядку. Есть задача: сделать мигающий компонент (и другие задачи подобного класса). В данном случае мы работаем с видом и это свойство вида, а не модели. Решить задачу можно через НОС, можно через директиву. На практике — решают ее через директиву. Почему?

> Учтите, что базовый компонент уже аннотирован Component, а WithBlink оборачивать нельзя, он же «принимает компонент — возвращает компонент»

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

> Мне кажется, вы лихо сели в оборону и съехали на «HOC» в ангуляре, тогда как

Я вам констатирую один простой наблюдаемый факт: есть класс задач, которые в ангуляре решаются как при помощи HOC, так и при помощи директив. При этом люди выбирают директивы. На основании этого я делаю гипотезу: директивы являются значительно более хорошим решением. Альтернативная гипотеза — существую какие-то иные причины. ОЧевидна, одна из этих причин верна. Я этих иных причин не вижу, вы их привести не можете. Отсюда я делаю вывод, что таких причин нет и верна первая гипотеза.

> жду обоснований, пока только вода

«Антипаттерн» — это распространенный подход к решению каких-либо задач, который является по каким-то причинам неудачным. Использование HOC невыразительно, негибко, ведет к повышению связности когда, его усложнению, копипасте и снижению качества кода в целом. Оно нарушает большую часть принципов SOLID. То, что паттерн плох, подтверждается наблюдениями — при наличии альтернативных вариантов решения люди по возможности избегают делать HOC и используют более высокоуровневые, более развитые подходы (например, директивы).

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

Но мы же рассуждаем о концепции в целом?

Information

Rating
Does not participate
Registered
Activity