从服务中获取对象以允许使用 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 以更好地了解如何使用它。
这可能是由于我缺乏理解,因为我是 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 以更好地了解如何使用它。