Сделано. Допускаю какие-то огрехи. Но я с лихвой вышел за пределы бюджета времени, который планировал выделить на это тестовое. Поэтому полировать и допиливать не буду. На тесты - извините, тоже, не буду тратить время. Делал не по TDD.
- Проект должен быть обернут в docker
- Пользователь должен иметь возможность пройти тест от начала до конца и в конце увидеть два списка - вопросы на которые он ответил верно и вопросы, где ответы содержали ошибки.
- Должна быть возможность пройти тест сколько угодно раз
- Каждый результат тестирования должен сохраняться в БД (выводить результаты не обязательно)
- (Не обязательно) И вопросы, и ответы для каждого вопроса должны показываться пользователю в случайном порядке при каждой новой серии тестирования
Скорее всего вы ожидаете, что-то более похожее на симфоневский шаблон, поэтому немного поясню поход.
Я фокусируюсь на границах:
- между ядром и второстепенными модулями,
- между бизнес логикой и инфраструктурным кодом.
В папке Contracts
определен контракт для системы тестирования. Контракт реализует ядро системы тестирования в папке TestingSystem
. Использует его единственный клиент, - консольное приложение-тестер в папке ConsoleTester
.
Я не заморачивался контрактом. Это простой класс с примитивными типами, и массивами на входе и на выходе, вместо строгих типов.
Ядро в TestingSystem
разделено на слои и придерживается гексагональной архитектуры. Я выделяю:
- Слой доменной модели, где собственно модель предметной области, в котором чистый PHP код, с несколькими допущениями:
- разрешаю
webmozart/assert
- разрешаю использовать
doctrine/collections
интерфейс - разрешаю использовать
Doctine ORM
аннотации
- разрешаю
- Слой
Application
, границы которого у меня обычно размыты, но как правило у меня там определены порты в терминах гексагональной архитектуры, какие-то сервисы, юзкейсы и та бизнес логика приложения, которая в доменных моделях не уместна. - Слой инфраструктуры, где лежат конкретные реализации инфраструктурных зависимостей вроде доктриновских реализаций репозиториев и любого другого кода специфичного для фреймворка или прочих библиотек. В терминах гексагональной архитектуры это адаптеры для портов, определенных в слое Application.
В итоге c таким подходом у нас есть:
- независимое ядро с бизнес логикой, в которое ясно-понятно инжектятся все необходимые инфраструктурные зависимости. Такое ядро будет удобно в дальнейшем сопровождать и разрабатывать.
- Ядро определяет контракт, который дальше может использовать любой клиент, я не стал делать веб, мне и консольки достаточно. Такая архитектура позволит вам легко подключить любой тип клиента, хоть веб, хоть телеграм, хоть веб.
make up
make cli
bin/console app:apply-fixtures
bin/console app:test
Я не очень понял такое решение, в том числе из-за разных подслоев, исключений в папках с энтити и так далее.
@bravik: там нет никаких исключений в папках с энтити 🤷, но даже если бы и были, то доменная модель имеет право на DomainException
Безусловно это не самое плохое решение, но в итоге я дал посмотреть это все ребятам, участвующим в найме с кейсом как им и готовы ли они дальше с этим работать. Общий вердикт был скорее нет. Это не все замечания, были и просто комментарии, что что-то не дотащили по архитектурной части. Что именно - я уточнять уже не буду - у ребят есть чем заниматься.
Начну с плюсов: -Структура Application, Domain, Infrastructure - это ок. В целом понятно. Правда с оговоркой на ConsoleTester, которая не вписывается в имеющуюся структуру директорий (слои) -Скрываем EM за интерфейсами flusher и в репозиториях имеется метод add - это плюс -Типизированные массивы в док блоках -Фикстуры в консольной команде - в целом, ок -Отдельный тип для UUID - правда с оговоркой, что он везде один и тот же, поэтому в данном случае как будто избыточен и можно было обойтись без обертки над Uuid
Что смутило:
Отсутствие тестов. Да, мы их прямо не требуем в задании, но задание подразумевает в том числе подумать над вещами, которые связаны с ним. Иными словами, мы смотрим как человек решает задачу в целом, делает ли что-то за рамками или обдумывает корнер-кейсы и альтернативные решения. Тесты у нас - маст хэв и пишутся к любой задаче, поэтому их отсутствие воспринимается скорее как минус.
@bravik: Тесты я действительно не стал делать, так как грохнул на задание сильно больше запланированного времени.
В ConsoleTester - мы используем EM напрямую, хотя скрыли его за интерфейсом NotFoundException - отнаследован от RuntimeException - это нехорошо, т.к. php библиотеки вынуждают нас считать RuntimeException unchecked исключением
@bravik: где они там энтити менеджер увидели ?! 🤷 Там используется 1 интерфейс - контракт ядра системы тестирования... пояснить где не захотели
Обилие массивов вместо понятных структур данных (DTO) Не уверен, что минус, но я не совсем понял - разбор входных параметров вручную. https://github.com/bravik/textmagic-test/blob/master/src/ConsoleTester/Model/TestSessionFactory.php#L11 Хочется валидировать с помощью ассертов - это ок. Но прямо из массива это дело разбирать - как будто бы перебор. Хотя и ассерты отключаются в разных окружениях, в итоге такой способ не очень подходит проекту с долгим сроком жизни, где в данных на проде может проскочить то, что не должно.
@bravik: почему использованы массивы, а не DTO я указал в README - не захотел тратить время в рамках тестового. Суть контракта от этого не меняется. Попросил уточнить что вообще значит "ассерты отключаются в разных окружениях", почему именно такой способ не подходит для проекта с долгим сроком жизни? Решили не уточнять, отмахнулись...
https://github.com/bravik/textmagic-test/blob/master/src/ConsoleTester/Commands/TestCommand.php#L44 Если говорить крупными мазками, честно говоря, тяжело читать это всё. Стоило бы разбить на методы и понятные сервисы Отдельно про continue 2 - субъективно, но в таких случаях приходится проверять уровни вложенности, чтобы понимать как дальше поведет себя код, возвращаться наверх и тд. Фактически вариант с переменной в while субъективно более понятен
@bravik: в целом понятно, но это чисто косметический момент. Для кейса в котором 2-3 состояния в switch не смочь прочитать - ну это несерьезно.
FQCN вместо use https://github.com/bravik/textmagic-test/blob/master/src/ConsoleTester/Commands/ApplyFixturesCommand.php#L117
@bravik: 🤯
Также в оценке тестового мы смотрим и на другие факторы - насколько и в чем человек заморочился, учел ли допы, прислал через день или за пять минут до дедлайна и тд - это все также влияет на итоговую оценку.
@bravik: Задание было прислано на третий день после интервью, срок на выполнение был неделя. Прислано полностью рабочее решение со всеми допами. Без тестов. С пояснениями.
Поэтому по общей сумме факторов и сравнивая с тестовыми заданиями других кандидатов, к сожалению, здесь и сейчас, не готов идти дальше. В любом случае, спасибо за потраченное время, надеюсь, обратная связь окажется полезной.
@bravik: признаться, это была наверное самая бесполезная обратная связь, которую мне давали на интервью.
Нужно сделать простую систему тестирования, поддерживающую вопросы с нечеткой логикой и возможностью выбора нескольких вариантов ответа.
“2 + 2 = ”
- 4
- 3 + 1
- 10
Правильными ответами тут будут 1 ИЛИ 2 ИЛИ (1 И 2). При этом любые другие комбинации (например, 1 И 3) не будут считаться верными, несмотря на то, что содержат правильный ответ.
- Cсылка на GitHub / Bitbucket с кодом и инструкцией по разворачиванию проекта
- Проект должен быть обернут в docker
- Пользователь должен иметь возможность пройти тест от начала до конца и в конце увидеть два списка - вопросы на которые он ответил верно и вопросы, где ответы содержали ошибки.
- Должна быть возможность пройти тест сколько угодно раз
- Каждый результат тестирования должен сохраняться в БД (выводить результаты не обязательно)
- (Не обязательно) И вопросы, и ответы для каждого вопроса должны показываться пользователю в случайном порядке при каждой новой серии тестирования
- Задание нужно выполнять с использованием Symfony и PostgreSQL
- Внешний вид не важен, авторизация не нужна, админка не нужна, достаточно разово добавить вопросы с ответами в БД
- Вопросы придумывать не нужно, можно взять список ниже
1 + 1 =
- 3
- 2
- 0
2 + 2 =
- 4
- 3 + 1
- 10
3 + 3 =
- 1 + 5
- 1
- 6
- 2 + 4
4 + 4 =
- 8
- 4
- 0
- 0 + 8
5 + 5 =
- 6
- 18
- 10
- 9
- 0
6 + 6 =
- 3
- 9
- 0
- 12
- 5 + 7
7 + 7 =
- 5
- 14
8 + 8 =
- 16
- 12
- 9
- 5
9 + 9 =
- 18
- 9
- 17 + 1
- 2 + 16
10 + 10 =
- 0
- 2
- 8
- 20
Правильный ответ: 4