2
votes

I'm fairly new to using Moq and Nunit for unit testing and I'm having issues with one scenario. What I want is for my mock to have an out parameters which my system under test will then use to decide what action to take.

My system under test is an MVC API controller and in particular I'm trying to test the POST method. I want to return an error message for the object when validation fails.

Here is the method code for the controller:

        public IHttpActionResult Post(Candidate candidate)
    {
        try
        {
            if(candidate==null)
                return BadRequest();

            IEnumerable<string> errors;
            _candidateManager.InsertCandidate(candidate, out errors);

            if (errors!=null && errors.Any())
                return BadRequest(CreateErrorMessage("Invalid candidate: ", errors));

            return CreatedAtRoute("DefaultApi", new {id = candidate.CandidateId}, candidate);

        }
        catch (Exception)
        {
            return InternalServerError();
        }
    }

This is my Unit Test Code:

        [Test]
    [Category("CandidateManagerController Unit Tests")]
    public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
    {
        IEnumerable<string> errors = new List<string>() {"error1", "error2"};

        var mockManager = new Mock<ICandidateManager>();
        mockManager.Setup(x => x.InsertCandidate(new Candidate(), out errors)).Callback(()=>GetErrors(errors));

        var sut = new CandidateManagerController(mockManager.Object);

        var actionResult = sut.Post(new Candidate());

        Assert.IsInstanceOf<BadRequestResult>(actionResult);

    }

What I expect is that when _candidateManager.InsertCandidate() is run then the errors variable is populated. However what is happening is that when you step through the controller code errors is null after _candidateManager.InsertCandidate() method is run.

If anyone has any ideas what I'm doing wrong or if what I want to do is not possible using Moq then please let me know.

Thanks

2
You have to set errors. What is the implementation of GetErrors? - Jeroen Heier

2 Answers

1
votes

What you want to do is possible. If you look at the Quickstart docs at https://github.com/Moq/moq4/wiki/Quickstart, there is a section where it shows how you do setups for methods using out params. I have made two corrections to your code and it works.

  1. You have to use the same candidate instance for both the mock setup and when you exercise the sut. Otherwise, Moq thinks that the two objects are different and your test setup becomes useless.
  2. You don't have to use Callback in order to set the errors returned by the mocked CandidateManager. Below is your test method with my changes.

    [Test]
    [Category("CandidateManagerController Unit Tests")]
    public void Should_Return_Bad_Request_When_Creating_Invalid_Candidate()
     {
    IEnumerable<string> errors = new List<string>() {"error1", "error2"};
    
    //instance to be used for both setup and test later
    var candidate = new Candidate(); 
    
    var mockManager = new Mock<ICandidateManager>();
    
    //removed Callback
    mockManager.Setup(x => x.InsertCandidate(candidate, out errors));
    
    var sut = new CandidateManagerController(mockManager.Object);
    
    var actionResult = sut.Post(candidate);
    
    Assert.IsInstanceOf<BadRequestResult>(actionResult);
    
     }
    
0
votes

You have to make sure that when you call your SUT that you use the same instance passed to the out argument otherwise the call will fail.

In your example, the method under test passes a null instance into the mocked method thus negating the setup of the test.

If however you are not able to supply the same instances for the out then it doesn't look like you will be able to get a mock to pass successfully. Take a look a the Quick Start for Moq to get an understanding of it capabilities.