简体   繁体   中英

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

This is probably due to my lack of understanding as I'm new to xUnit and Moq but I'm trying to test a post method in my controller, however in the controller, I have this line of code:

var user = await _mapModelService.MapModelForNewUser(viewModel);

This will pass the ViewModel to the service where it maps the ViewModel properties to a new Model ready for it to be written to the database later.

In the test, I'm trying to use Moq to use the service and return a new Model. This code I have is like so:

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

All I need is the id so I can test the route values once it's finished, however the user object in the controller is null and the test fails with a NullReferenceException. I'm not trying to test the object the service returns, it's just so the test runs.

Where have I gone wrong?

EDIT: The suggested answer does not answer my question, it appears to be very similar to what I'm already doing. I've tried creating new instances of ViewModel and Model and giving them arbitrary values and passing those instead, with the same result.

EDIT 2: The service is asynchronous, id that makes a difference how this is called but I was under the impression ReturnsAsync() handles this.

EDIT 3: Here's the full test.

        [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"]);
        }

If you do not care about the viewmodel being passed in then use It.IsAny<NewUserVm>() in the setup.

Also the Setup needs to be done before invoking the subject under test

// 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

// ...

The original setup was done with a specific instance that will never be used when exercising the test. Thus the mock will never behave as you would expect.

Reference Moq Quickstart for a better understanding of how to use it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM