Pull to refresh
67
0
alek_sys @alek_sys

User

Send message
Представьте себе простейший компонент, используемый для аутентификации, в котором есть поле для ввода имени пользователя и кнопка отправки данных. В поле вводят имя, нажимают на кнопку, это вызывает выполнение запроса fetch с введёнными данными. Запрос оказывается выполненным, имя передаётся в хранилище Redux и кэшируется в localStorage. В общем-то, всё это тоже не так уж и сложно.
Теперь попытаемся отправить этот компонент на тестовый рендеринг. К сожалению, тест даже не запустится.

Если этот компонент тестировать "одним куском" (и гордо "не используя shallow рендеринг") — тогда конечно, тест не запустится. Более того, без бэкенда он никогда работать не будет. Только вот отношения к юнит-тестам такой тест тоже не имеет.


Но если разбить этот мега-компонент на составные части, то получится вполне разумная картинка (которую можно "нарисовать" через TDD):


  1. В поле вводим имя, нажимаем на кнопку — это вызывает метод сервиса signIn (мока, разумеется). Все, больше тест ничего не тестирует.
  2. В сервисе вызываем метод signIn, проверяем, что он вызывает fetch или мок http-клиента
  3. Еще в сервисе тестируем, что при успешном ответе от мока fetch или http-клиента — он вызывает нужный метод хранилища.
  4. В метода хранилища — смотрим что он вызывает кеширование...

И т.д.


А если для работы теста нужны reduxState, url, localStorage, fetch и т.п. — может стоит задуматься, а не слишком ли много ответственностей у тестируемого компонента?

Кстати, да, "суррогат" создало в целом скорее негативный фон для статьи, хотя тема важная.


Хочется заметить по поводу


"В вашем подходе fail fast, fail cheap есть 2 недостатка: с одной стороны он дорогой, т.к. время тратится на разработку того, что можно было бы не делать, с другой стороны он способствует написанию плохого кода, появлению костылей и вообще ведёт к некачественному продукту".

На это в современных lean практиках есть решения:


  • дороговизна эксперимента (а вообще если убрать свою терминологию то это и есть lean experiment) решатся за счет практик Lean UX Design и User Centric Design, т.е. любое решение начинается с проблемы пользователя и через "дешевый" прототип (банальный wireframe) валидируется на пользователях, до того как уходит в разработку
  • плохой код и костыли — TDD + CI + постоянный рефакторинг

В современной практике продуктового дизайна "fail fast" называется Lead Product Development. Он неплохо описан в книге "The Lean Startup". Несмотря на название, там не только про стартапы и не только для стартаперов.

Функция apply тут не при чем, метод может называться как угодно. Любой интерфейс с единственным методов (SAM, single abstract method) может быть функциональным интерфейсом. И документация про это говорит: "Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted".

Я бы предложил пару вещей, которые можно сделать со статьей, чтобы она больше соответствовала формату:


  1. Оформлять блоки кода как блоки кода
  2. Пометить статью как "Tutorial", т.к. это базовые знания по Java и будут полезны начинающим
  3. Лично я бы убрал загадочные формулы с синусами и упоминание карринга, это совершенно нерелевантно
  4. Быть более снисходительным к документации, вещи вроде "И все бы хорошо, да худо только в том, что прийти к этому решению рациональным путем просто невозможно" не совсем отражают положение вещей. Мне кажется, любой, кто использует Java 8 больше чем пару дней знает про функциональные интерфейсы и они описаны в ссылке на документацию, которая приводится в статье — Function прямо говорит, что это Functional interface.

Пара полезных (надеюсь) вещей по тестам:


  1. FixMethodOrder – не уверен, насколько полезно, я бы понял если случайный порядок, но намеренно по порядку?
  2. @AutoconfigureMockMvc и потом
    @Autowired
    lateinit var mockMvc: MockMvc
  3. MockMvc не нужен baseUrl, относительные пути прекрасно распознаются — и не надо переживать про виртуальный путь
  4. Для JSON есть отличный хелпер, который помогает работать с JSON используя JSONPath, а не сравнение со строкой. Например:
    mockMvc.perform(get("/"))
                .andExpect(jsonPath("$.*.name", hasItems("iPhone 4s")))

Спасибо за статью! Хотя я не очень понимаю целевую аудиторию — совсем новичкам в реактивном программировании она ничего не объяснит, а людям, знакомым с RxJava или Reactor ничего нового не даст.


Пара вещей, которые стоит отметить, говоря про реактивный Spring:


  • Spring MVC не использован т.к. он построен на спецификации сервлетов, которые (пока) не особо реактивные
  • Помимо WebFlux реактивный Spring включает еще и реактивный доступ к данным (Mongo, Redis), безопасность (Spring Reactive Security) — ведь если все контроллеры реактивные, но доступ к базе блокирующий — то смысла в реактивности особо нет

И важный момент насчет тестов — тестирование кода фреймворка или вообще любого внешнего кода это не всегда хорошая идея. Хотя в статье это иллюстрация работы Mono / Flux, но в целом тестировать функции just и map не нужно вне контекста вашего кода.

Начиная с версии 5 Spring вполне умеет быть реактивным.


Более того, корутины без проблем живут в Spring — т.к. он использует Project Reactor для асинхронности, а у kotlinx.coroutines есть поддержка Reactor.


Вообще, странно противопоставлять "спринг", Jetty и/или корутины, это как бы ортогональные вещи.

Хороший обзор, спасибо.


только сервер аутентификации и сервер приложения знают секретный ключ

Cекретный ключ они знают только если сервер аутентификации это и есть сервер приложения (т.е. это физически одно и то же приложение), если же они разные — то скорее будет использована пара private / public key. Вряд ли сервер аутентификации будет смело делиться со всеми серверами приложений своим приватным ключом.

Спасибо за статью, интересное применение antlr. А почему готовая грамматика не подошла?

Преимущества абстракций liquibase это:


  • независимость (теоретически) от базы, liquibase генерит нужный sql код для конкретной базы. "Теоретическая" — потому, что такого требования часто нет, да и периодически все равно требуется что-то делать чистым sql, что уже ломает автоматическую совместимость
  • возможность автоматического rollback, для большинство вещей типа создания таблиц, ключей и т.п. liquibase может автоматически создать "обратную" операцию и откатить изменения. Не то, чтобы их сложно было написать в SQL, но все же

Это вариант Spring 4 и Spring Boot

Эта статья будет не полной без самого популярного комментария из обсуждения на Reddit:


Let me sum up.
"We're JS twinks with no real experience with backend software dev, so instead of learning a new language that's well proven on the backend like Ruby or Python, we picked a JavaScript tool with almost no track record as a backend platform. Then we realized it sucks for a huge number of backend applications! So naturally…
1) we picked yet another completely unproven, alpha quality platform… 2) that nobody else uses in production, so support is hard to come by when it breaks,… 3) with a custom syntax (even though we picked node instead of a good platform specifically to avoid learning a new syntax)… 4) AND WE REWROTE ALL OUR SHIT BECAUSE YOLO"
Whoever makes tech decisions for this team is an irresponsible, incompetent idiot who has no business being near production systems.

Спасибо за статью, хороший анализ процессов, но все же остается несколько неоднозначное впечатление… Даже сам очень формальный и "казенный" язык (простите, если это сделано намеренно) как-то не вяжется с "гибким" подходом и people over processes. Agile это же не про спринты и команду, сидящую вместе, это просто один из компонентов. Agile это изменение в ментальности и процессах вообще. И главное это уход от мысли "Мы точно знаем, какой продукт должен быть на выходе".


Гибкий процесс как раз уходит от этого постулата и взамен говорит "Давайте работать гибко и реагировать на потребности пользователей", а например ни один из ваших процессов не вовлекает… конечных пользователей.


Многие большие компании и банки пытаются внедрить скорее wagile (waterfall + agile), т.е. утвердить scope, требования и иногда даже сроки заранее, а внутри просто разбить разработку на спринты, упуская важный момент, что цель спринта (вообще итерации) и частой доставки продукта это получить обратную связь от пользователей и понять, а то ли мы вообще делаем? Если разработка идет спринтами, но поставка для конечных пользователей делается через два года разработки, то есть ли вообще ценность в спринтах?


Я крайне рекомендую книгу The Lean Startup, Эрик Рейс там много рассуждает, как agile и lean подходы могут работать в больших предприятиях.

Только в битве за RAM не забывайте, что вообще весь Spring сам по себе, занимает не так уж и много памяти — до 32Мб. Есть интересное исследование на эту тему от Дейва Сайера.

Реактивные подходы они скорее не для производительности (она теоретически может быть даже хуже для отдельных запросов), а для конкурентности — т.е. обработать больше клиентов одновременно.

Если "они" это Spring, а не команда вашего проекта — то нет, не будут. Более того, REST и WebFlux они ортогональны, т.е. REST не надо "переписывать" на WebFlux, REST можно запускать поверх WebFlux, но не обязательно. Все старые парадигмы остаются.

Предполагает. Это не очень видно, но он возвращает не ответ типа T, а Mono<T> или Flux<T> — реактивный тип из Project Reactor.

Конфигурация DI кодом уже есть в Spring 5 и называется Programmatic Bean Registration и даже есть удобный Kotlin DSL для него.

Думаю, это актуально только для десктопной Java и апплетов (которые уже не поддерживаются), которое я бы отнес к крайним случаям.


Если же говорить про серверную часть, которой все-таки большинство в мире Java, я вот после предыдущего комментария задумался, что я за уже много лет в индустрии я не видел, что несколько приложений деплоились в общую среду и могли шарить JVM. Обычно это либо контейнер, либо виртуалка, либо выделенная железяка per app. Так что для таких случаев выбор нужной версии Java не проблема.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity