8

How do I set up my test method on that mocks a repository which accepts an object?

This is what I have so far:

Service.cs

    public int AddCountry(string countryName)
    {
        Country country = new Country();
        country.CountryName = countryName;
        return geographicsRepository.SaveCountry(country).CountryId;
    }

test.cs

    [Test]
    public void Insert_Country()
    {
        //Setup
        var geographicsRepository = new Mock<IGeographicRepository>();

        geographicsRepository.Setup(x => x.SaveCountry(It.Is<Country>(c => c.CountryName == "Jamaica"))); //How do I return a 1 here?

        GeographicService geoService = new GeographicService(geographicsRepository.Object);

        int id = geoService.AddCountry("Jamaica");

        Assert.AreEqual(1, id);
    }

SaveCountry(Country country); returns an int.

I need to do 2 things:

  1. First test, I need to tell the setup to return an int of 1.
  2. I need to create a second test Insert_Duplicate_Country_Throws_Exception(). In my Setup, how do I tell the repository to throw an error when I do:

    int id = geoService.AddCountry("Jamaica");
    int id = geoService.AddCountry("Jamaica");
    

Framework:

  1. NUnit.
  2. Moq.
  3. ASP.NET MVC - repository pattern.
Shawn Mclean
  • 53,945
  • 92
  • 265
  • 401

2 Answers2

8

Your first test should look something like this:

[Test]
public void Insert_Country()
{
    Mock<IGeographicRepository> geographicsRepository = new Mock<IGeographicRepository>();
    GeographicService geoService = new GeographicService(geographicsRepository.Object);

    // Setup Mock
    geographicsRepository
        .Setup(x => x.SaveCountry(It.IsAny<Country>()))
        .Returns(1);

    var id = geoService.AddCountry("Jamaica");

    Assert.IsInstanceOf<Int32>(id);
    Assert.AreEqual(1, id);
    geographicsRepository.VerifyAll();
}

The second test should look like this:

[Test]
public void Insert_Duplicate_Country_Throws_Exception()
{
    Mock<IGeographicRepository> geographicsRepository = new Mock<IGeographicRepository>();
    GeographicService geoService = new GeographicService(geographicsRepository.Object);

    // Setup Mock
    geographicsRepository
        .Setup(x => x.SaveCountry(It.IsAny<Country>()))
        .Throws(new MyException());

    try
    {
        var id = geoService.AddCountry("Jamaica");
        Assert.Fail("Exception not thrown");
    }
    catch (MyException)
    {
        geographicsRepository.VerifyAll();
    }
}
Tyler Treat
  • 13,862
  • 13
  • 74
  • 112
  • Shouldn't I call `AddCountry("Jamaica")` twice and setup the repository to watch if the same string was passed twice? – Shawn Mclean Dec 19 '10 at 02:58
  • @Shawn: In general, mock objects are designed to allows developers to test application layers independently. In this case, you're testing the service layer independent of your DAO. All you should be doing is checking that the service layer is indeed calling the proper methods. The repository unit tests should, presumably, be responsible for verifying that exceptions are being thrown when they should be. – Tyler Treat Dec 19 '10 at 03:18
  • ok, that makes sense. So in essence, I dont really need a second test for that service, only for mocking the repository. Thank you. – Shawn Mclean Dec 19 '10 at 03:20
  • No problem. For a really good explanation of this train of thought, read here: http://stackoverflow.com/questions/3622455/what-is-the-purpose-of-mock-objects/3623574#3623574 – Tyler Treat Dec 19 '10 at 03:24
  • Its true mock object are designed to test application layers independently, I am agree with this. but in real scenario its not happen, developer wants to test complete method along with its dependency, hence I think mocking are not suitable in that case. and also we should write mock for those method which have high cost for maintain, ie. dbcall, etc. – Shobhit Walia Dec 27 '16 at 07:26
1

I think maybe you are slightly misunderstanding the purpose of testing with mocks in the two scenarios you have supplied.

In the first scenario, you wish to test that 1 is returned when you pass in "Jamaica". This is not a mock test case but a test case for real behaviour as you wish to test a specific input against an expected output i.e. "Jamaica" -> 1. In this situation mocking is more useful to ensure that internally your service calls SaveCountry on the repository with the expected country, and that it returns the value from the call.

Setting up your "SaveCountry" case and then calling "VerifyAll" on your mock is the key. This will assert that "SaveCountry" was indeed called with country "Jamaica", and that the expected value is returned. In this way you have confidence that your service is wired up to your repository as expected.

[Test]
public void adding_country_saves_country()
{
    const int ExpectedCountryId = 666;

    var mockRepository = new Mock<IGeographicRepository>();

    mockRepository.
      Setup(x => x.SaveCountry(It.Is<Country>(c => c.CountryName == "Jamaica"))).
      Returns(ExpectedCountryId); 

    GeographicService service= new GeographicService(mockRepository.Object);

    int id = service.AddCountry(new Country("Jamaica"));

    mockRepo.VerifyAll();

    Assert.AreEqual(ExpectedCountryId, id, "Expected country id.");
}

In the second scenario you wish to test that an exception is raised when you attempt to add a duplicate country. There's not much point in doing this with a mock as all you will test is that your mock has behaviour when adding duplicates, not your real implementation.

Tim Lloyd
  • 36,374
  • 9
  • 92
  • 127
  • Thank you. I understand the part about not checking for a value. In your example, You do not return a value (the CountryId). In the Method of AddCountry, it returns the Id of that of the repository. Should I still mock a return value because it is needed? – Shawn Mclean Dec 19 '10 at 03:52
  • @Shawn My appologies - getting late here... Yes mocking of the return value should be in there. I have updated my answer. – Tim Lloyd Dec 19 '10 at 04:02
  • Thank you. The more examples there are is the more I understand these patterns :) – Shawn Mclean Dec 19 '10 at 04:11
  • This can be cleaned up further with AutoFixture and Moq customization for AutoFixture. – John Zabroski Mar 13 '19 at 01:45