简体   繁体   中英

How to unit test Command Handler in CQRS pattern in C#

I'm learning and practicing CQRS and Unit Test in C#. I need to know how to unit test Command Handlers in CQRS.

Here is my CreateAuthorCommand :

public sealed class CreateAuthorCommand : ICommand<Author>
{
    public CreateAuthorCommand(string firstName, string lastName, DateTimeOffset dateOfBirth, string mainCategory, IEnumerable<CreateBookDto> books)
    {
        FirstName = firstName;
        LastName = lastName;
        DateOfBirth = dateOfBirth;
        MainCategory = mainCategory;
        Books = books;
    }

    public string FirstName { get; }
    public string LastName { get; }
    public DateTimeOffset DateOfBirth { get; private set; }
    public string MainCategory { get; }
    public IEnumerable<CreateBookDto> Books { get; }

    [AuditLog]
    [DatabaseRetry]
    internal sealed class AddCommandHandler : ICommandHandler<CreateAuthorCommand, Author>
    {
        private readonly IUnitOfWork _unitOfWork;

        public AddCommandHandler(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
        }

        public async Task<Result<Author>> Handle(CreateAuthorCommand command)
        {
            var nameResult = Name.Create(command.FirstName, command.LastName);
            var birthDateResult = BirthDate.Create(command.DateOfBirth);
            var mainCategoryResult = Entities.Authors.MainCategory.Create(command.MainCategory);

            var authorResult = Result.Combine(nameResult, birthDateResult, mainCategoryResult)
                                .Map(() => new Author(nameResult.Value, birthDateResult.Value, null, mainCategoryResult.Value));
                
            if (authorResult.IsFailure)
                return Result.Failure<Author>(authorResult.Error);

            await _unitOfWork.AuthorRepository.AddAsync(authorResult.Value);

            await _unitOfWork.SaveChangesAsync();

            return Result.Success(authorResult.Value);
        }
    }
}

Here is my Unit Test:

public class CreateAuthorCommandTests
{
    [Fact]
    public void Create_Author_Should_Call_Add_Method_Once() 
    {
        var fixture = new Fixture();
        var command = fixture.Create<CreateAuthorCommand>();
        var mockUnitOfWork = new Mock<IUnitOfWork>();
        var mockHandler = new Mock<ICommandHandler<CreateAuthorCommand, Author>>();

        var result = mockHandler.Object.Handle(command);

        mockUnitOfWork.Verify(x => x.AuthorRepository.AddAsync(It.IsAny<Author>()), Times.Once);
    }
}

When I debug the above test,

  1. I'm not able to step into Handler Method!!
  2. How to pass mockUnitOfWork as constructor parameter to AddCommandHandler ?

If I can pass mockUnitOfWork , the I can verify the AddAsync call inside my AuthorRepository . Please assist on what I'm doing wrong.

You are testing the handler, so instead of this

var result = mockHandler.Object.Handle(command);

...create an actual instance of AddCommandHandler and inject the dependencies it requires, ie

var mockUnitOfWork = new Mock<IUnitOfWork>();
var handler = new AddCommandHandler(mockUnitOfWork.Object);

When you call in to Handle , you'll now be stepping into your implementation.

If you want to keep the Internal access modifier, you can use InternalsVisibleTo attribute to grant access to your test project, eg do this in your project that defines the Handler,

[assembly: InternalsVisibleTo(“UnitTests”)]

https://anthonygiretti.com/2018/06/27/how-to-unit-test-internal-classes-in-net-core-applications/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM