views:

72

answers:

6

Often times, a class would not have a direct output; it manipulates its dependencies, making them do things.

For example:

internal interface IEmailer
{
    void SendEmail();
}

class ReportSender
{
    public IEmailer Emailer { get; set; }

    public ReportSender(IEmailer emailerToUse)
    {
        Emailer = emailerToUse;
    }

    public void SendReport()
    {
        // Do whatever is needed to create the report

        Emailer.SendEmail();
    }
}

Creating a mock of IEmailer and make it expect IEmailer.SendEmail() seem to be exposing too much of the innards of the class and making the test fragile. But I can't think of any other way to test this class.

How should we write unit tests for such a class?

A: 

I'm not very experienced with unit testing yet, so this might not be too helpful... but it seems to me your suggestion of mocking out the dependency is on the right track. You can then probe the state of the mocked dependencies after the operation completes, to determine whether or not the IEmailer performed the expected manipulations on them?

kander
+2  A: 

Using Mock objects is the correct way to write the unit test for this method. You don't want your test to actually send an email. Mock objects let you break dependencies between different classes for the purposes of unit testing.

Here's an example using Rhino Mocks.

http://www.ayende.com/projects/rhino-mocks.aspx

IEmailer emailer = MockRepository.GenerateMock<IEmailer>();
emailer.Expect(x => x.SendEmail());

ReportSender reportSender = new ReportSender();
reportSender.Emailer = emailer;
reportSender.SendReport();

emailer.VerifyAllExpectations();
jdot
+1  A: 

Creating a mock of IEmailer and make it expect IEmailer.SendEmail()

is exactly the way you would test that.

since these are UNIT tests, you are not exposing too much. you are supposed to be exercising all the functions of the unit to be tested, and it's alright if you have internal knowledge of the unit.

dave thieben
A: 

Creating a mock IEmailer and expecting a call to SendEmail is the proper way to test this scenario.

Ian P
+2  A: 

Making a mock of IEmailer doesn't by itself expose too much of the class. Rather, it makes it open for extensibilty.

There's a certain tendendency that heavily relying on interaction-based testing (i.e. mocks) makes tests more fragile, but this is more of a design issue than an issue with mocks.

The Hollywood Principle can be really helpful here, because if your dependencies are mostly designed around void methods, dynamic mocks like Moq or Rhino Mocks will generally just ignore the method calls unless you specifically tell them to care about a particular one.

Mark Seemann
I'm concerned about the fragility of the test. What do you think is the right way to design the test and/or class?
rgunawan
I think your current IEmailer example is pretty spot on. Obviously a real definition would likely take some input in the SendEmail method, but apart from that it's good because it returns void. This means that modern Dynamic Mocks will simply provide a default implementation of the method if not explicitly told otherwise. Nothing breaks.
Mark Seemann
But how would I test ReportSender without making Mock<IEmailer> expect SendMail? If I don't do that, wouldn't my test be not valuable (because it doesn't test what ReportSender should do)? But, if I do, wouldn't that make my test more fragile?
rgunawan
You are referring to what is known as *strict* mode where a dynamic mock will throw on any unexpected usage. This leads to very fragile tests, so modern dynamic mock libraries only support this mode for backwards compatibility. They default to a loose mode where they will attempt to handle any call to the best of their ability. This is why void methods are so attractive, because a modern mock will just allow the method to be called, and since it doesn't need to return anything, the call will always succeed. In short, that particular issue is just not an issue any longer.
Mark Seemann
A: 

Creating a mock of IEmailer and make it expect IEmailer.SendEmail() seem to be exposing too much of the innards of the class and making the test fragile.

Then why are you exposing a public property requiring an internal interface?

Make the interface public and your implementations of them internal.

Randolpho