Search code examples
c#unit-testingnunitrepository-patternrhino-mocks-3.5

Handling Multiple Mocks and Asserts in Unit Tests


I currently have a repository that is using Entity Framework for my CRUD operations.

This is injected into my service that needs to use this repo.

Using AutoMapper, I project the entity Model onto a Poco model and the poco gets returned by the service.

If my objects have multiple properties, what is a correct way to set-up and then assert my properties?

If my service has multiple repo dependencies what is the correct way to setup all my mocks? * - A class [setup] where all the mocks and objects are configured for these test fixtures?*****

I want to avoid having 10 tests and each test has 50 asserts on properties and dozens on mocks set-up for each test. This makes maintainability and readability difficult.

I have read Art of Unit Testing and did not discover any suggestions how to handle this case.

The tooling I am using is Rhino Mocks and NUnit.

I also found this on SO but it doesn't answer my question: Correctly Unit Test Service / Repository Interaction

Here is a sample that expresses what I am describing:

public void Save_ReturnSavedDocument()
{
    //Simulate DB object
    var repoResult = new EntityModel.Document()
        {
            DocumentId = 2,
            Message = "TestMessage1",
            Name = "Name1",
            Email = "Email1",
            Comment = "Comment1"
        };

    //Create mocks of Repo Methods - Might have many dependencies
    var documentRepository = MockRepository.GenerateStub<IDocumentRepository>();
    documentRepository.Stub(m => m.Get()).IgnoreArguments().Return(new List<EntityModel.Document>()
        {
           repoResult
        }.AsQueryable());

    documentRepository.Stub(a => a.Save(null, null)).IgnoreArguments().Return(repoResult);

    //instantiate service and inject repo
    var documentService = new DocumentService(documentRepository);
    var savedDocument = documentService.Save(new Models.Document()
        {
            ID = 0,
            DocumentTypeId = 1,
            Message = "TestMessage1"
        });

    //Assert that properties are correctly mapped after save
    Assert.AreEqual(repoResult.Message, savedDocument.Message);
    Assert.AreEqual(repoResult.DocumentId, savedDocument.DocumentId);
    Assert.AreEqual(repoResult.Name, savedDocument.Name);
    Assert.AreEqual(repoResult.Email, savedDocument.Email);
    Assert.AreEqual(repoResult.Comment, savedDocument.Comment);
    //Many More properties here
}

Solution

  • First of all, each test should only have one assertion (unless the other validates the real one) e.q. if you want to assert that all elements of a list are distinct, you may want to assert first that the list is not empty. Otherwise you may get a false positive. In other cases there should only be one assert for each test. Why? If the test fails, it's name tells you exactly what is wrong. If you have multiple asserts and the first one fails you don't know if the rest was ok. All you know than is "something went wrong".

    You say you don't want to setup all mocks/stubs in 10 tests. This is why most frameworks offer you a Setup method which runs before each test. This is where you can put most of your mocks configuration in one place and reuse it. In NUnit you just create a method and decorate it with a [SetUp] attribute.

    If you want to test a method with different values of a parameter you can use NUnit's [TestCase] attributes. This is very elegant and you don't have to create multiple identical tests.

    Now lets talk about the useful tools.

    AutoFixture this is an amazing and very powerful tool that allows you to create an object of a class which requires multiple dependencies. It setups the dependencies with dummy mocks automatically, and allows you to manually setup only the ones you need in a particular test. Say you need to create a mock for UnitOfWork which takes 10 repositories as dependencies. In your test you only need to setup one of them. Autofixture allows you to create that UnitOfWork, setup that one particular repository mock (or more if you need). The rest of the dependencies will be set up automatically with dummy mocks. This saves you a huge amount of useless code. It is a little bit like an IOC container for your test.

    It can also generate fake objects with random data for you. So e.q. the whole initialization of EntityModel.Document would be just one line

    var repoResult = _fixture.Create<EntityModel.Document>();
    

    Especially take a look at:

    • Create
    • Freeze
    • AutoMockCustomization

    Here you will find my answer explaining how to use AutoFixture.

    SemanticComparison Tutorial This is what will help you to avoid multiple assertions while comparing properties of objects of different types. If the properties have the same names it will to it almost automatically. If not, you can define the mappings. It will also tell you exactly which properties do not match and show their values.

    Fluent assertions This just provides you a nicer way to assert stuff. Instead of

    Assert.AreEqual(repoResult.Message, savedDocument.Message);
    

    You can do

    repoResult.Message.Should().Be(savedDocument.Message);
    

    To sum up. These tools will help you create your test with much less code and will make them much more readable. It takes time to get to know them well. Especially AutoFixture, but when you do, they become first things you add to your test projects - believe me :). Btw, they are all available from Nuget.

    One more tip. If you have problems with testing a class it usually indicates a bad architecture. The solution usually is to extract smaller classes from the problematic class. (Single Responsibility Principal) Than you can easily test the small classes for business logic. And easily test the original class for interactions with them.