views:

345

answers:

1

Hi there,

I am new to Mocking and somewhat familiar with unit testing and finally have decided to bite the bullet on a new project starting up front with a strict TDD approach. However I have one service class and method I need to retrospectively add tests to as it has been promoted from a prototype.

I do not know where to start though on this particular test, this is the classes and methods involved:

public class PageService : IPageService
{
    private readonly ITestService testServiceClient;

    public PageService(ITestService testServiceClient)
    {
        this.testServiceClient = testServiceClient;
    }

    public Page GetPage(Guid websiteId, string pageKey)
    {
        Page builtPage = null;

        // WCF SERVICE CALL I DO NOT WANT EXECUTING WHEN RUNNING UNIT TEST
        // BUT RATHER WANT A BLANK NEW INSTANCE OF "PAGE" CREATED USING MOQ??
        var page = testServiceClient.GetPage(websiteId, pageKey);

        if (page == null)
            return null;

        builtPage = new Page();

        [code here to build builtPage if input params ok] ...

        return builtPage;
    }
}

What I am endeavouring to do, is write one test, from this hopefully I can expand out all permutations of GetPage(...) tests, but for the first one simply test if a valid websiteId and pageKey has been passed in, if so receive a valid Page instance back and assert true to the unit test.

testServiceClient is a WCF service client, this was wrapped in a using() statement before, but I have moved it out of that in hope to get it going with dependency injection as I feel that will be the first step required for testing, as from what I understand, I will need to give it a fake / mocked? wcf client instead where testServiceClient.GetPage() returns a known in-memory set of data? Hopefully I am on the right track there.

So here is my initial blueprints of my unit test (I am using Moq framework, Setup() is the new version of Expect() if you haven't used latest ver of Moq):

/// <summary>
/// Tests service returns a valid instance of type `Page` when a valid website and valid page key is tested.
/// </summary>
[TestMethod]
public void Test_Valid_Website_And_Valid_PageKey_Returns_Valid_Instance_Of_Page()
{
    // Arrange
    Page page = null;

    // Act
    var newPage = new Page() { Title = "Mocked Version!?"};

    testServiceClient = new Mock<ITestService>();
    testServiceClient.Setup(x => x.GetPage(websiteId, "about-us")).Returns(newPage);

    service = new PageService(testServiceClient.Object);
    page = service.GetPage(websiteId, "about-us");

    // Assert
    Assert.IsInstanceOfType(page, typeof(Page), "Object was not of expected instance type.");
}

I have no idea where to go from here, or if I am on the right track? I can confirm the above has no syntax errors, and I do receive an exception:

System.ArgumentException: Invalid setup on a non-overridable member:
x => x.GetPage(websiteId, "about-us").

All I know is that I want my service.GetPage(...) to return a new instance, as websiteId and pageKey was valid, however I don't want it to use the real testServiceClient.GetPage() WCF call... hopefully I understand the idea of mocking correctly. Have I told it correctly through Moq when you use testServiceClient.GetPage on that service, actually just return a new instance of page?

Any clarification is greatly appreciated! Thanks guys!

+1  A: 

I would say that you are on the right track. If ITestService truly is an interface (as indicated by its name), you will be able to Mock it.

However, when you define your Setups, Moq must be able to identify which method you meant, and it seems as though you somehow indicate a method that doesn't exist on the interface.

You didn't show us the ITestService interface, and neither do we know the type of the websiteId variable.

If you take a closer look at the Setup method you will see that it's really a generic method, so there's type inferencing going on when you call it without the generic parameters.

My guess is that the declaration of websiteId somehow clashes with the declaration of GetPage. If, for example, websiteId was declared as object, Moq would look after a method with this signature:

Page GetPage(object x, string y);

which isn't the same as

Page GetPage(Guid x, string y);

In any case, once you manage to get the Setup to work, your basic idea about the unit test seems fine. You can do more than just asserting that the instance isn't null, because the Mock will ensure that the instance returned is the same as the instance you originall set up:

[TestMethod]
public void Test_Valid_Website_And_Valid_PageKey_Returns_Valid_Instance_Of_Page()
{
    // Arrange
    Page page = null;

    // Act
    var newPage = new Page() { Title = "Mocked Version!?"};

    testServiceClient = new Mock<ITestService>();
    testServiceClient.Setup(x => x.GetPage(websiteId, "about-us")).Returns(newPage);

    service = new PageService(testServiceClient.Object);
    page = service.GetPage(websiteId, "about-us");

    // Assert
    Assert.AreEqual(newPage, page);
}

Notice the much more valuable Assert.

Mark Seemann
+1 Nice answer - @GONeale, just to confirm what Mark says, I have run a quick test using MOQ and an Interface that returns a page - it works fine. It would seem that you are instead doing something like mockiing a concrete instance where GetPage is not declared virtual.
David Hall
Hi guys, thanks for taking the time to read my post. Well ITestService is a WCF Generated Service Client, (the right click project -> add service reference) type. In light of this, I don't think I can declare it as `virtual` as it's a auto-generated class. I could however, create a simple WCF caller class, and make this virtual, would that help do you think? Thanks for the Assert tip.
GONeale
Do I understand correctly in saying by declaring `testServiceClient.Setup(x => x.GetPage(..)).Returns(newPage)` tells any call to a name of that method (nested even as an object's method passed in through as a param such as the example of `new PageService(testServiceClient.Object)`) to actually not run the *actual* (wcf) method, but just return a `newPage` instance instead? Is that why `virtual` is required, it overrides it with totally new method contents?
GONeale
Excuse my ignorance of mocking, I am trying to catch up, however it's a delicate balance at my workplace here between getting this project out the door vs. time allowed to research. Typical story.
GONeale
w00t!!! I created a `WcfPageService` and declared a `GetPage()` virtual method which did the WCF round trip, then used that as my mockable object! Worked like a charm!! Thanks guys, you can review the applicable code here http://pastebin.com/f848e6da
GONeale
@GONeale: Good to hear that you are making progress. Most of what you've been guessing at above is correct. You can only mock abstract and virtual members on classes, as well as all members of interfaces. I'm not sure I understand why ITestService isn't an interface though... I understand that it's WCF auto-generated, but AFAIR, WCF auto-generates an interface as well. In any case, creating a wrapper like your WcfPageService to deal with non-overridable types is exactly the right strategy to use :)
Mark Seemann
Well mainly if it's auto generated I can't make it `virtual`.
GONeale
@GONeale: You don't have to make it `virtual` if it's an interface, but you are saying that it's a concrete class despite its name?
Mark Seemann
Oh, sorry Mark. Yes WCF did make me a `TestService` and `ITestService`, but every time I tried to mock the `TestService.GetPage(..)` it threw up the exception I stated in my initial question :( But the abstracted, 'virtualised' WcfTestService worked. Might have another look when I have more time.
GONeale