从服务中获取对象以允许使用 xUnit 和 Moq 测试 运行

Get an object from a service to allow test to run using xUnit and Moq

这可能是由于我缺乏理解,因为我是 xUnit 和 Moq 的新手,但我正在尝试在我的控制器中测试 post 方法,但是在控制器中,我有这一行代码:

var user = await _mapModelService.MapModelForNewUser(viewModel);

这会将 ViewModel 传递给服务,在该服务中它将 ViewModel 属性映射到新模型,以便稍后将其写入数据库。

在测试中,我尝试使用 Moq 来使用服务和 return 新模型。我的这段代码是这样的:

mapModelService.Setup(x => x.MapModelForNewUser(new NewUserVm())).ReturnsAsync(new User()
{
    UserId = 1
});

我只需要 id,这样我就可以在完成后测试路由值,但是控制器中的用户对象为 null,并且测试失败并出现 NullReferenceException。我不是要测试服务 returns 的对象,它只是让测试运行。

我哪里做错了?

编辑:建议的答案没有回答我的问题,它似乎与我已经在做的非常相似。我试过创建 ViewModel 和 Model 的新实例并给它们任意值并传递这些值,结果相同。

编辑 2:服务是异步的,id 会影响它的调用方式,但我的印象是 ReturnsAsync() 处理这个。

编辑 3:这是完整的测试。

        [Fact]
        public async Task NewUser_ShouldRedirectToNewCreatedUserWhenModelStateIsValid_WithUserDetailsVm()
        {
            // Arrange
            var logger = new Mock<ILogger<UserController>>();
            var vmService = new Mock<IViewModelService>();
            var userRepo = new Mock<IUserRepo>();
            var mapModelService = new Mock<IMapModelService>();

            var userController = new UserController(logger.Object, vmService.Object, userRepo.Object, mapModelService.Object);
            var redirectToRouteResult = await userController.NewUser(It.IsAny<NewUserVm>()) as RedirectToRouteResult;
            

            // Act
            mapModelService.Setup(x => x.MapModelForNewUser(It.IsAny<NewUserVm>()))
                .ReturnsAsync(new User()
                {
                    UserId = 1
                });

            userController.ModelState.Clear();

            // Assert
            Assert.NotNull(redirectToRouteResult);
            Assert.False(redirectToRouteResult.Permanent);
            Assert.Equal("UserDetails", redirectToRouteResult.RouteValues["Action"]);
            Assert.Equal("User", redirectToRouteResult.RouteValues["Controller"]);
            Assert.Equal(1, redirectToRouteResult.RouteValues["id"]);
            Assert.Equal("note", redirectToRouteResult.RouteValues["requiredTab"]);
        }

如果您不关心传入的视图模型,请在设置中使用 It.IsAny<NewUserVm>()

此外,Setup 需要在调用被测对象之前完成

// Arrange

// ...omitted for brevity

mapModelService
    .Setup(x => x.MapModelForNewUser(It.IsAny<NewUserVm>())) //<--NOTE THE ARGUMENT MATCHER
    .ReturnsAsync(new User() {
        UserId = 1
    });

var viewModel = new NewUserVm();

// Act
var redirectToRouteResult = await userController.NewUser(viewModel) as RedirectToRouteResult;
       
// Assert

// ...

最初的设置是使用一个特定实例完成的,在进行测试时永远不会使用该实例。因此模拟永远不会像你期望的那样。

参考 Moq Quickstart 以更好地了解如何使用它。