c# - 如何在 DDD 命令处理程序中测试逻辑?

标签 c# unit-testing domain-driven-design clean-architecture

我正在尝试学习 DDD 和 Clean Architecture 的一些想法,但遇到了一个问题:如何对位于应用程序层中的命令处理程序进行单元测试,验证它们在域对象上调用正确的方法,而不测试其中的逻辑那些域对象?
假设我有以下域实体:

public class User
{
    public User(int id)
    {
        Id = id;
    }

    public int Id { get; }

    public void RemoveProfilePicture()
    {
        ...
    }
    ...
}
我有以下简单的 Command 类:
public class RemoveUserProfilePictureCommand : ICommand
{
    public RemoveUserProfilePictureCommand(int userId)
    {
        UserId = userId;
    }
    public int UserId { get; }
}
和一个命令处理程序(位于应用层):
public class Handler : ICommandHandler<RemoveUserProfilePictureCommand>
{
    private readonly IUserRepository userRepository;

    public Handler(IUserRepository userRepository)
    {
        this.userRepository = userRepository;
    }

    public void Handle(RemoveUserProfilePictureCommand command)
    {
        var user = userRepository.GetById(command.UserId);
        user.RemoveProfilePicture();
    }
}
我想验证对 Handle 的调用会找到合适的User并调用域方法 RemoveProfilePicture在上面。我发现的唯一两个选项不能满足我,我正在寻找更好的解决方案。
基于领域逻辑副作用的断言
显而易见的解决方案是断言对域模型的操作发生了,例如:
[Fact]
public void Handle_UserExists_ShouldRemoveProfilePicture()
{
    var user = new User(id: 555);
    repository.GetById(user.Id).Returns(user);
    var command = new RemoveUserProfilePictureCommand(user.Id);

    handler.Handle(command);

    Assert.Null(user.ProfilePicture);
} 
我使用这种方法的问题是我们根据域模型内部的逻辑进行断言,如果该逻辑比设置 ProfilePicture 更复杂属性(property)到null我们仍然需要在命令处理程序的测试中断言它的结果,即使域逻辑已经被它自己的单元测试覆盖。问题源于应用层类与域类的紧密耦合。这让我想到了第二种解决方案:
将应用层与领域层解耦
如果User类将实现一个接口(interface),比如 IUser ,那么测试中的假存储库可能会返回 IUser 的不同实现到验证调用了正确方法的命令处理程序。这里的问题是,根据我的理解,应用程序层应该是一个围绕域的薄包装器,不应与其解耦。此外,我发现的所有示例都始终使用域对象的具体类型,并且在实体类中实现接口(interface)似乎很奇怪。

任何人都可以看到测试此类类的更好解决方案吗?因为它不是关于某些边缘情况,它是关于几乎每个 Command Handler 类,而且它们中的大多数都比我上面给出的简单示例更复杂。

最佳答案

当我在做一个受 DDD 启发的 CQRS 项目时,我也遇到了完全相同的问题。据我所知,您也在尝试开发 CQRS 项目,因为您正在使用诸如命令之类的概念并且坚持要返回 null命令处理程序中的值。
话虽如此,断言 null值(value)不测试任何东西。您要测试的是系统中发生了副作用。在您的情况下,您希望确保“已删除用户个人资料图片”。
您似乎缺少的是 CQRS 的另一半:事件。当命令处理程序成功处理其事务(即副作用)时,您通常会调度一个代表该事务的事件。然后,您的单元测试将简单地断言 UserProfilePictureRemoved事件已调度(或入队,取决于您的实现)。

关于c# - 如何在 DDD 命令处理程序中测试逻辑?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62683580/

相关文章:

domain-driven-design - 事件溯源 - 如何恢复错过的事件

C# json反序列化对象

c# - 如何为将在多个步骤中创建的聚合建模,例如向导样式

c# - 在现有接口(interface)上应用 ServiceContract 和 OperationContract

java - 为单元测试序列化对象

java - 接口(interface)模拟工作不正常

java - 你能模拟一个使用 Mockito 调用静态方法的非静态方法吗?

domain-driven-design - 微服务之间基于事件的通信没有共享事件库?

c# - 有没有第三方文件上传工具可以上传大于2GB的文件?

c# - 一行中的线程安全单例属性