【问题标题】:How to do BDD using CQRS?如何使用 CQRS 进行 BDD?
【发布时间】:2018-11-09 10:32:18
【问题描述】:

我使用 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(它只有命令),第二个有很多返回读取模型的查询方法(用于 UI 的 dtos)。

因此,我有带有 Task 实体的 TaskRepo - 没有 getter 的业务对象和到 dto 的映射方法(如前所述)和带有返回 dtos 的简单搜索方法的 QueryTaskRepository。

在集成测试中它工作得很好,因为我使用真正的数据库,但在单元测试中,我注入了一个使用 HashMap 的简单内存存储库实现。没有 CQRS 很容易测试,因为我有一个模型和数据源(一个带有一个哈希映射的 repo),但是现在 InMemoryQueryTaskRepository 对 InMemoryTaskRepository 中的状态一无所知。

你有什么想法如何测试它(单元)?

【问题讨论】:

    标签: java domain-driven-design bdd cqrs


    【解决方案1】:

    我现在已经开始使用 CQRS,但我不知道如何测试它。为什么?

    听起来你缺少一块。

    在集成测试中它工作得很好,因为我使用真正的数据库,但在单元测试中,我注入了一个使用 HashMap 的简单内存存储库实现。没有 CQRS 很容易测试,因为我有一个模型和数据源(一个带有一个哈希映射的 repo),但是现在 InMemoryQueryTaskRepository 对 InMemoryTaskRepository 中的状态一无所知。

    因此,您需要考虑两种模式中的一种,具体取决于哪种模式更接近您的实际预期设计。

    1) 如果您希望所有存储库都与一个“数据库”通信,那么在您的测试中,您的存储库应该与内存数据库中的 same 通信(又名,相同的哈希映射)。

    2) 如果您期望有两个(或更多)“数据库”,其中“读取”存储库与一个数据库通信,而“写入”存储库与另一个数据库通信,那么将会有一些复制过程/projects 从写入数据库到读取数据库的信息,并且该投影需要在您的“单元测试”中表示。

    从您描述集成测试的方式来看,我怀疑第一种情况更合适。

    如果 Task 没有 getter 和 toDto 方法,我如何将 Task 从 Task 的 HashMaps 映射到任何读取模型?在真正的数据库中,我只能选择/加入数据,但在 Java 中?

    如何从Task 中获取数据以将其存储在数据库中?使用它。

    因此,根据您的建议,任务(作为域对象)应该具有 toDbRow() 方法,该方法产生表示 DB 行的 dto 是

    拼写toDbRow 表示您将接口耦合到特定的数据库表示。更接近于描述当前状态的与存储无关的值。

    对于 API,它可能是一个零参数查询,或者它可能接受一个函数,该函数接受一些作为参数的值并返回一些有用的东西。

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

    【讨论】:

    • 是的,我想使用第一个,但是如果 Task 没有 getter 和 toDto 方法,我如何将 Task 从 Tasks 的 HashMaps 映射到任何读取模型?在真正的数据库中,我只能选择/加入数据,但在 Java 中?也许反思:D
    • 到目前为止我已经使用了 Spring Data + JPA 所以.. 我知道,业务对象不是 jpa 实体。它之所以有效,是因为 JPA 使用反射。因此,根据您的建议 Task (作为域对象)应该有 toDbRow() 方法,该方法产生 dto 表示 DB 行是吗? :) 听起来很合理。
    • 您应该从聚合中引发 domainEvents 并更新内存预测
    • 我有一个 DB - 用于写入和读取,因此我不需要发出事件和更新。
    • 我无法想象这个。您能否在空闲时间描述一个您有任务并希望将其保存到数据库的示例?如果这个类没有 getter 和 toDto() 方法,我怎么知道 Task 的状态?
    猜你喜欢
    • 2020-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-18
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    相关资源
    最近更新 更多