1

I'm new to unit testing, so my problem is probably with my code and not the Moq framework, but here goes.

I'm using .Net Core with xUnit and the Moq framework, and I'm more or less following instructions from their documentation. I'm trying to test route api/user to get all users, and the issue was on asserting that the response was an ObjectResult containing <IEnumerable<User>>. No matter what I tried, result.Value was always null. The first assertion passes fine.

I set up a console project to debug this, and found something interesting. that value of the controller in the test method in Visual Studio is null. In VS Code, the value in the debugger shows Unknown Error: 0x00000....

Below is the test:

public class UserControllerTests {

    [Fact]
    public void GetAll_ReturnsObjectResult_WithAListOfUsers() {
        // Arrange
        var mockService = new Mock<IUserService>();
        var mockRequest = new Mock<IServiceRequest>();
        mockService.Setup(svc => svc.GetUsers(mockRequest.Object))
            .Returns(new ServiceRequest(new List<User> { new User() }));
        var controller = new UserController(mockService.Object);

        // Act
        var result = controller.GetAll();

        // Assert
        Assert.IsType<ObjectResult>(result);
        Assert.IsAssignableFrom<IEnumerable<User>>(((ObjectResult)result).Value);
    }

}

And here is the controller:

public class UserController : Controller {

    private IUserService service;

    public UserController(IUserService service) {
        this.service = service;
    }

    [HttpGet]
    public IActionResult GetAll() {
        var req = new ServiceRequest();
        service.GetUsers(req);

        if(req.Errors != null) return new BadRequestObjectResult(req.Errors);
        return new ObjectResult(req.EntityCollection);
    }

}

And the Service Layer:

public interface IUserService {
    IServiceRequest GetUsers(IServiceRequest req);
}    
public class UserService : IUserService {       
    private IUserRepository repo;       
    public IServiceRequest GetUsers(IServiceRequest req) {
        IEnumerable<User> users = null;         
        try {
            users = repo.GetAll();
        }
        catch(MySqlException ex) {
            req.AddError(new Error { Code = (int)ex.Number, Message = ex.Message });
        }
        finally {
            req.EntityCollection = users;
        }
        return req;
    }
}

public interface IServiceRequest {
    IEnumerable<Object> EntityCollection { get; set; }
    List<Error> Errors { get; }
    void AddError(Error error);
}
public class ServiceRequest : IServiceRequest {
    public IEnumerable<Object> EntityCollection { get; set; }
    public virtual List<Error> Errors { get; private set; }

    public ServiceRequest () { }

    public void AddError(Error error) {
        if(this.Errors == null) this.Errors = new List<Error>();
        this.Errors.Add(error);
    }       
}

Like I said, it's probably something I'm doing wrong, I'm thinking in the mockService.Setup() but I'm not sure where. Help please?

7
  • Something seems off between the method under test and your moq setup. what is service.GetUsers(req); suppose to do? Commented Oct 14, 2016 at 1:30
  • @Nkosi There's a service layer between the controller and the repository. req is just a container for the <List>User and/or <List>Error. So basically that line should just return an object containing a List of Users. Commented Oct 14, 2016 at 1:45
  • But you are not returning anything there. looks like you are populating the req variable. Commented Oct 14, 2016 at 1:48
  • Your implementation of UserService.GetUsers is not returning anything. Is that a typo? Commented Oct 14, 2016 at 2:22
  • Yes it was, sorry Commented Oct 14, 2016 at 2:32

1 Answer 1

1

From the use of service.GetUsers(req) it looks like service is suppose to populate the service request but in your setup you have it returning a service request. A result which is also not used according to your code.

You need a Callback to populate whatever parameter is given to the service in order to mock/replicate when it is invoked. Since the parameter is being created inside of the method you will use Moq's It.IsAny<> to allow the mock to accept any parameter that is passed.

var mockService = new Mock<IUserService>();
mockService.Setup(svc => svc.GetUsers(It.IsAny<IServiceRequest>()))
    .Callback((IServiceRequest arg) => {
        arg.EntityCollection = new List<User> { new User() };
    });

This should allow the method under test to flow through it's invocation and allow you to assert the outcome.

Sign up to request clarification or add additional context in comments.

5 Comments

req was getting populated by reference from that call. I tried this, it gives this error on dotnet test: System.ArgumentException : Invalid callback. Setup on method with parameters (IServiceRequest) cannot invoke callback with parameters (ServiceRequest).
Ok you need to provide more information about the ServiceRequest and IUserService. Update your original question with a minimal reproducible example
Updated with the service layer. This still gives the same error, but for Setup on method with parameters (IServiceRequest).
@neilsimp1, I rebuilt a test with the information provided, including my answer and test passed. Where are you getting the error? Look at my updated answer
This did the trick, the test is now passing. Thank you so much, Nkosi! It's still strange the controller value was showing null/Unknown Error, I guess I just wasn't understanding Moq enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.