我正在尝试学习 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/