I am trying to learn some of the ideas of DDD and Clean Architecture and I came across a problem: how do I unit test Command Handlers that live in the Application Layer, verifying they call the right methods on domain objects, without testing the logic within those domain objects? Let's say I have the following domain entity:
public class User
{
public User(int id)
{
Id = id;
}
public int Id { get; }
public void RemoveProfilePicture()
{
...
}
...
}
And I have the following simple Command class:
public class RemoveUserProfilePictureCommand : ICommand
{
public RemoveUserProfilePictureCommand(int userId)
{
UserId = userId;
}
public int UserId { get; }
}
And a Command Handler (located in the Application Layer):
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();
}
}
I would like to verify that a call to Handle will find the right User and call the domain method RemoveProfilePicture on it. The only two options I found do not satisfy me and I am looking for a better solution.
Asserting Based on the Side Effects of the Domain Logic
The obvious solution is to assert that the action on the domain model occurred, something like:
[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);
}
The problem I have with this approach is that we make the assertions based on the logic inside the domain model, and if that logic was more complex than setting the ProfilePicture property to null we would still have to assert its results in the tests of the Command Handler, even though the domain logic is already covered by its own unit tests. The problem arises from the tight coupling of the Application Layer classes to the Domain classes. Which led me to the second solution:
Decoupling the Application Layer from the Domain Layer
If the User class would implement an interface, say IUser, then the fake repository in the test could return a different implementation of IUser to the Command Handler that verifies the correct method was called. The problem here is that from my understanding, the Application Layer should be a thin wrapper around the domain and should not be decoupled from it. Also, all the examples I found always used concrete types for Domain Objects, and it does seem odd to implement an interface in an entity class.
Can anybody see a better solution for testing such classes? because it's not about some edge case, it's about almost every Command Handler class, and most of them are more complex than the simple example I gave above.