views:

70

answers:

2

We are using Moq to unit test our service classes, but are stuck on how to test situations where a service method calls another service method of the same class. I tried setting the method being called to virtual, but still couldn't figure out what to do then in Moq. For example:

public class RenewalService : IRenewalService
{
    //we've already tested this
    public virtual DateTime? GetNextRenewalDate(Guid clientId)
    {
        DateTime? nextRenewalDate = null;
        //...<snip> a ton of already tested stuff...

        return nextRenewalDate;
    }

    //but want to test this without needing to mock all 
    //the methods called in the GetNextRenewalDate method
    public bool IsLastRenewalOfYear(Renewal renewal)
    {
        DateTime? nextRenewalDate = GetNextRenewalDate(renewal.Client.Id);
        if (nextRenewalDate == null)
            throw new Exceptions.DataIntegrityException("No scheduled renewal date, cannot determine if last renewal of year");
        if (nextRenewalDate.Value.Year != renewal.RenewDate.Year)
            return true;
        return false;
    }
}

In the above example, our GetNextRenewalDate method is pretty complicated, and we've already unit tested it. However, we want to test the simpler IsLastRenewalOfYear without needing to mock everything needed for GetNextRenewalDate. Basically, we just want to mock GetNextRenewalDate.

I realize that I could create a new class that overrides GetNextRenewalDate and test the new class, but is there a way that I can leverage Moq to make this simpler?

A: 

Edit: I now agree that this is not the right answer for Andrew's case. I want to leave this answer here for the comments thread. Please don't down vote any more :)

Before edit:

Generally mock object frameworks aren't designed to simplify the single class scenario, they're designed to isolate your code so you can test a single class.

If you try to use a mock object framework to solve this, the framework is just going to create a derived class and overload that method. The only thing different will be that you can do that in about 3 lines instead of 5, because you won't have to create the derived class definition.

If you'd like to use mock objects to isolate this behavior, then you should split up this class a bit. The GetNextRenewalDate logic could live outside the RenewalService object.

The fact that you're running into this problem may show that there is a simpler or more finely grained design yet to be discovered. Finding a class that is a bit less concrete, with a name like "manager" or "service" is often a hint that you could break up your design into smaller classes and get better reusability and maintainability out of it.

Merlyn Morgan-Graham
I'm not sure if breaking up our design would make it more maintainable ... if we went from 5,000 classes (approximately what we currently have) to 10,000 classes, it would hardly be easier to navigate.
Andrew
@Merlyn - breaking up the design simply to make it more testable within a specific toolset seems like a bad idea. Going down this path, you'd rarely have methods call other public methods within a class. How is that good design?
Jess
@Andrew, Jess: I don't know his app or his requirements, so feel free to take or leave my advice. Yes, breaking it up just to have one-method classes is silly, but the rest of my advice still holds. Often times when I've seen a "manager" or "service" class, the abstractions aren't in the right places. If such a refactor were to be made, methods from other classes might end up here, or these methods might end up in other classes, making this class disappear completely.
Merlyn Morgan-Graham
@Andrew, Jess: As for answering the question, I guess I should have made this a comment :) I think I assumed that by `create a new class that overrides GetNextRenewalDate`, he meant ditching Moq for this class, and writing the mocks/test doubles himself. I can see now that this probably wasn't what he meant.
Merlyn Morgan-Graham
I took away my downvote - your edit makes sense. I agree that there are times to refactor, but if within-class dependencies is one of the criteria then we'd all have an insane (and hard-to-manage) proliferation of classes.
Andrew
+3  A: 

You can probably use partial mocking in this scenario, although all your methods would need to be virtual:

    var mock = new Moq.Mock<RenewalService>();
    mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
    mock.CallBase = true;
    var results = mock.Object.IsLastRenewalOfYear(...);
Jess
This seems to work ... I didn't even need to make everything virtual (I just kept it the same as above).
Andrew
@Jess, @Andrew. The above works because `Mock.CallBase == true` means that invocations that are not matched by a setup will call the underlying implementation. So, `IsLastRenewalOfYear` will call the implementation, because it's not virtual, but `GetNextRenewalDate` will return `null` because the setup will always be matched. The following code will work even if `IsLastRenewalOfYear` **is** virtual.
Igor Zevaka
@Jess - we used this in a couple other places too, worked like a charm. Thanks!
Andrew
@Jess - This saved me loads of time, thanks!
Deano
@Deano - yeah, I use it quite a lot now, pretty useful.
Jess