我们正在使用 CQRS/Mediator 模式,我正在尝试为 API 编写一些单元测试,我从控制器开始。
这是我想为其编写单元测试的 CreateAync() 方法。
[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;
}
这是 CreateCommand 类:
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; }
}
这是我的测试方法:
[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);
}
我得到 Null 异常,调试后我发现异常来自控制器中的 CreatAsync() 方法,看起来文章总是为空,我该如何解决这个“文章”空异常?
解决方案是嘲笑 Sender 而不是 Mediator。
首先必须在构造函数中注入ISender接口,然后在测试方法中使用它。
private readonly Mock<IUserService> _userService;
private readonly ArticlesController _articlesController;
private readonly Mock<ISender> _sender;
private readonly Mock<HttpContext> _httpContext;
public ArticlesControllerTests()
{
_sender = new Mock<ISender>();
_httpContext = new Mock<HttpContext>();
_httpContext.Setup(ctx=>ctx.RequestServices.GetService(typeof(ISender)))
.Returns(_sender.Object);
_currentUserMock = new Mock<ICurrentUser>();
_articlesController = new ArticlesController(_userService.Object);
var controllerContext = new ControllerContext
{
HttpContext = _httpContext.Object
};
_articlesController.ControllerContext = controllerContext;
}
HttpContext
然后启动controllerContext也很重要。
对于测试方法,我使用 _sender 而不是 _mediator :
//// Arrange
var tesId = Guid.NewGuid();
// -Create a test request
var testRequest = new CreateArticleCommand("Test Title", "Test Content", Guid.NewGuid(), ContentItemState.Draft, new List<ContentTagDto>());
// -Create a test article
var testArticle = new ArticleDto
{
Id = Guid.NewGuid(),
Title = testRequest.Title,
Content = testRequest.Content,
ContentCategoryId = testRequest.ContentCategoryId,
ContentState = testRequest.ContentState,
ContentTags = testRequest.ContentTags,
CreatedBy = tesId,
LastModifiedBy = tesId,
CreatedByUser = new UserDto { Id = tesId, Email = "[email protected]",
FirstName = "Test", LastName = "User" }
};
_sender.Setup(m => m.Send(
It.IsAny<CreateArticleCommand>(),
It.IsAny<CancellationToken>())).ReturnsAsync(testArticle);
//// Act
var result = await _articlesController.CreateAsync(new
CancellationToken(), testRequest);
//// Assert
Assert.NotNull(result);
现在这个测试方法工作正常,我很高兴我可以自己解决这个问题:D