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

问题描述

这可能是由于我不了解xUnit和Moq的原因,但是我试图在控制器中测试post方法,但是在控制器中,我有以下代码行:

var user = await _mapModelService.MapModelForNewUser(viewmodel);

这会将viewmodel传递给服务,在服务中它将viewmodel属性映射到一个新的Model,以便以后将其写入数据库

在测试中,我试图使用Moq来使用服务并返回新的Model。我的这段代码是这样的:

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

我所需要的只是id,因此我可以在完成路由后测试路由值,但是控制器中的用户对象为null,并且测试失败,并显示NullReferenceException。我并不是要测试服务返回的对象,只是为了使测试运行。

我哪里出错了?

编辑:建议的答案不能回答我的问题,它似乎与我已经在做的非常相似。我尝试过创建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,以更好地了解其用法。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...