We are using CQRS/Mediator patterns, I'm trying to write some unit tests for the APIs, I started by the controllers.
this is the method CreateAync() that I want to write unit test for.
[HttpPost]
[MustHavePermission(FSHAction.Create, FSHResource.Article)]
[OpenApiOperation("Create new Article.", "Content State => Draft = 0 , Published = 1")]
public async Task<ArticleDto> CreateAsync(CancellationToken cancellationToken, CreateArticleCommand request)
{
var article = await Mediator.Send(request, cancellationToken);
article.CreatedByUser = await _userService.GetAsync(article.CreatedBy.ToString(), cancellationToken);
article.LastModifiedByUser = await _userService.GetAsync(article.CreatedBy.ToString(), cancellationToken);
return article;
}
this is the CreateCommand class :
public class CreateArticleCommand : IRequest<ArticleDto>
{
public CreateArticleCommand(string title, string content, Guid contentCategoryId, ContentItemState contentState, List<ContentTagDto> contentTags)
{
Title = title;
Content = content;
ContentCategoryId = contentCategoryId;
ContentState = contentState;
ContentTags = contentTags;
}
public string Title { get; set; } = default!;
public string Content { get; set; } = default!;
public Guid ContentCategoryId { get; set; }
public ContentItemState ContentState { get; set; }
public List<ContentTagDto> ContentTags { get; set; }
}
this is my test method :
[Fact]
public async Task CreateAsync_ReturnsValidArticleDto()
{
// Arrange
var cancellationToken = CancellationToken.None;
var controllerContext = new ControllerContext
{
HttpContext = _httpContext.Object
};
_articlesController.ControllerContext = controllerContext;
var contentTag = new ContentTagDto(1, "test")
{
Id = 1,
Title = "test"
};
var contentTagDtoList = new List<ContentTagDto>() { contentTag };
var createArticleCommand = new CreateArticleCommand("Title", "content", Guid.NewGuid(), ContentItemState.Draft, contentTagDtoList);
var mediatorMock = new Mock<IMediator>(); // Replace with your actual IMediator interface
var userServiceMock = new Mock<IUserService>(); // Replace with your actual IUserService interface
// Set up the Mediator mock to return a dummy ArticleDto
mediatorMock
.Setup(m => m.Send(createArticleCommand, cancellationToken))
.ReturnsAsync(new ArticleDto { Id = Guid.NewGuid() });
// Set up the UserService mock to return a dummy user
var user = new UserDto { Id = Guid.NewGuid(), Email = "[email protected]", FirstName = "Test", LastName = "User" };
_userService
.Setup(x => x.GetAsync(_currentUserMock.Object.GetUserId().ToString(), CancellationToken.None))
.ReturnsAsync(user);
// Act
var result = await _articlesController.CreateAsync(cancellationToken, createArticleCommand);
// Assert
Assert.NotNull(result);
}
I get Null Exception, after debuggin I discovered that the exception is from CreatAsync() method in the controller, looks like the article is always null, how can I solve this "Article" null exception ?
The solution was mocking Sender instead of Mediator.
First it's mandatory to inject the ISender interface in the constructor, then use it in the test method.
it's also important to
HttpContextand then initiate the controllerContext.for the test method I used _sender instead of _mediator :
Now this test method works fine and I'm pretty happy that I could solve this problem by myserlf :D