views:

192

answers:

2

Hi

I'm trying to write a test for an ASP.Net MVC controller action.

I'd like to test that the action invokes a particular method on an injected service, so I'm mocking the service and using .Verify.

So in the simple case, I have the following action in my controller:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(string title)
    {
        _cmsService.AddPage(title);

        return View("Edit");
    }

using the service interface...

public interface ICmsService
{
    void AddPage(string request);
}

and the following test...

        //Arrange
        const string pageTitle = "Test Page";

        var cmsService = new Mock<ICmsService>();

        cmsService.Setup(service => service.AddPage(pageTitle));

        var controller = new PageController(cmsService.Object);

        //Act
        var result = controller.Create(pageTitle) as ViewResult;

        //Assert
        cmsService.Verify(service => service.AddPage(pageTitle), Times.Once());

Now I want to refactor my service operation to use request and response objects...

public interface ICmsService
{
    CmsServiceAddPageResponse AddPage(CmsServiceAddPageRequest request);
}

So I change my action accordingly...

    public ActionResult Create(string title)
    {
        var request = new CmsServiceAddPageRequest()
                          {
                              PageName = title
                          };

        var response = _cmsService.AddPage(request);

        return View("Edit");
    }

and also my test...

        //Arrange
        const string pageTitle = "Test Page";

        var cmsService = new Mock<ICmsService>();

        var request = new CmsServiceAddPageRequest() {PageName = pageTitle};

        cmsService.Setup(service => service.AddPage(request));

        var controller = new PageController(cmsService.Object);

        //Act
        var result = controller.Create(pageTitle) as ViewResult;

        //Assert
        cmsService.Verify(service => service.AddPage(request), Times.Once());

But now when I run the test, I get the following message...

TestCase 'Web.Test.PageControllerTest.CreateNewPagePost'
failed: Moq.MockException : 
Invocation was performed more than once on the mock: service => service.AddPage(value(Web.Test.PageControllerTest+<>c__DisplayClass1).request)
    at Moq.Mock.ThrowVerifyException(IProxyCall expected, Expression expression, Times times)
    at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
    at Moq.Mock.Verify[T,TResult](Mock mock, Expression`1 expression, Times times, String failMessage)
    at Moq.Mock`1.Verify[TResult](Expression`1 expression, Times times)
    PageControllerTest.cs(67,0): at Web.Test.PageControllerTest.CreateNewPagePost()

What should I be doing to test a method that accepts a non-primitive type?

Thanks

Sandy

+1  A: 

If you override Equals in the CmsServiceAddPageRequest object it should compare them correctly.

Adam Driscoll
Thanks. Though that means I'm changing my code to suit the testing tool (albeit in a relatively harmless way), which doesn't seem right.Is that my only option?
sandy
Actually, I feel this is a less "hackey" way of doing it than using a custom matcher, at least for a primitive type. I've done a custom matcher for stuff like a list, or I want to check just a subset of properties...
Bryce Fischer
+3  A: 

I think a better alternative to the first answer would be to implement a custom matcher rather than change code to match your testing framework. From:http://code.google.com/p/moq/wiki/QuickStart

// custom matchers
mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>();
...
public string IsLarge() 
{ 
  return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100);
}
Adam Driscoll
Thanks, that seems more suitable.
sandy