Как сделать BDD с помощью CQRS? - PullRequest
0 голосов
/ 09 ноября 2018

Я тестирую свои приложения, используя BDD-подход. Пока это было просто, потому что у меня был один фасад и одно хранилище на модуль. Модульные тесты моего модуля (фасада) выглядели так:

В тесте:

       private TaskForm createSimpleTask() {
          return new TaskForm("Task1", "Text1");
       }


    // given

        TaskForm form = createSimpleTask();

    // when

        TaskDto savedTask = taskFacade.save(form);

    // then

        TaskWithUserDto loadedTask = taskFacade.load(savedTask.getId());
        assertNonNull(loadedTask);
        assertEquals(loadedTask.getTitle(), "Task1");
        assertEquals(loadedTask.getText(), "Text1");
        assertEquas(loadedTask.getAuthor(), "Michal");

В taskFacade:

   TaskDto save(TaskForm form) {
        UserDto currentUser = userFacade.getLoggedUser();
        Task task = taskFactory.create(form, currentUser.getId());
        return taskRepo.save(task).dto();
    }

    TaskWithUserDto load(long taskId) {
        Task task = taskRepo.findById(taskId);
        UserDto user = userFacade.load(task.getUserId());
        return new TaskWithUserDto(task.getTitle(), task.getText(), user.getName());
    }

Я начал использовать CQRS сейчас и не знаю, как тестировать систему с этой архитектурой. Почему?

Вместо одного фасада у меня есть CommandTaskFacade и QueryTaskRepository. Первый возвращает только long или void (он имеет только команды), а второй имеет множество методов запросов, которые возвращают модели чтения (dtos для пользовательского интерфейса).

Поэтому у меня есть TaskRepo с сущностью Task - бизнес-объект без методов получения и отображения на dto (как ранее) и QueryTaskRepository с простыми методами поиска, возвращающими dtos.

В интеграционных тестах это работает нормально, потому что я использую реальную БД, но в модульных тестах я внедряю простые реализации в хранилищах памяти, которые используют HashMap. Без CQRS это легко проверить, потому что у меня есть одна модель и источник данных (одно хранилище с одной хэш-картой), но теперь InMemoryQueryTaskRepository ничего не знает о состоянии в InMemoryTaskRepository.

Есть ли у вас какие-либо идеи, как проверить его (единицу измерения)?

1 Ответ

0 голосов
/ 09 ноября 2018

Я начал использовать CQRS сейчас и не знаю, как это проверить. Почему?

Звучит так, будто вы упустили кусок.

В интеграционных тестах он работает нормально, потому что я использую реальную БД, но в модульных тестах я внедряю простые реализации в хранилищах памяти, которые используют HashMap. Без CQRS это легко проверить, потому что у меня есть одна модель и источник данных (одно хранилище с одной хэш-картой), но теперь InMemoryQueryTaskRepository ничего не знает о состоянии в InMemoryTaskRepository.

Так что вам нужно подумать об одном из двух шаблонов, в зависимости от того, какой из них ближе к вашему фактическому заданному дизайну.

1) Если вы ожидаете, что будет единственная «база данных», с которой общаются все ваши репозитории, то в ваших тестах ваши репозитории должны общаться с той же в базе данных памяти (иначе та же хеш-карта).

2) Если вы ожидаете, что будет две (или более) «базы данных», с репозиторием «read», общающимся с одной базой данных, и репозиторием «write» с другой, тогда будет некоторый процесс, который копирует / проецирует информацию из базы данных записи в базу данных чтения, и этот прогноз должен быть выражен в вашем «модульном тесте».

Исходя из того, как вы описываете свой интеграционный тест, я подозреваю, что первый случай подходит ближе.

как мне сопоставить Task из HashMaps of Tasks с любой моделью чтения, если у Task нет методов получения и методов toDto? В реальной БД я могу просто выбрать / объединить данные, но в Java?

Как вы получаете данные из Task для хранения их в БД? Используйте это.

Поэтому, согласно вашему предложению, Task (как объект домена) должен иметь метод toDbRow (), который выдает dto, представляющий строку БД yes

Правописание toDbRow подразумевает, что вы связываете интерфейс с определенным представлением базы данных. Ближе будет значение, не зависящее от хранилища, которое описывает текущее состояние.

Что касается API, то это может быть запрос с нулевым аргументом, или он может принять функцию, которая принимает некоторые значения в качестве аргументов и возвращает что-то полезное.

<T> T currentState( MagicTaskStateHelper<T> helper )
...