4

I am trying to mock a call to a server and verify that the tested code called the correct method. Code structure is as follows:

public interface IServerAdapter
{
    void CallServer(Action<IServerExposingToClient> methodToCall, out bool serverCalled);
    object CallServer(Func<IServerExposingToClient, object> methodToCall, out bool serverCalled);
}

public interface IServerExposingToClient
{
    Resource GetResource(string id);
    void AddResource(Resource resource);
}

The code I'm testing accesses an implementation of IServerAdapter and calls IServerExposingToClient methods on the server. The reason for this is that we don't want to have to implement every single method (there are quite a lot of them) on the IServerExposingToClient in a class. Since this is then invoked on the proxy, it works quite well in our code.

Calling a method on the server is done like this:

_mainViewModel.ServerAdapter.CallServer(m => m.AddResource(resource), out serverCalled);

The problem now is testing and mocking. I need to assert that the method (AddResource) has been called on the server. The out serverCalled (problem 1) is to make sure the call has made it to the server so logic flows as it should for the caller.

When I use the following Moq setup, I can assert that some method has been called on the server:

Mock<IServerAdapter> serverAdapterMock = new Mock<IServerAdapter>();
bool serverCalled;
bool someMethodCalled = false;
serverAdapterMock.Setup(c => c.CallServer(It.IsAny<Action<IServerExposingToClient>>(), out serverCalled)).Callback(() => someMethodCalled = true);
// assign serverAdapterMock.Object to some property my code will use
// test code
Assert.IsTrue(someMethodCalled, "Should have called method on server.");

As the test says, I can assert that some method has been called on the server. The problem is that I don't know which method has been called. In some methods, the logic I want to test could take different paths depending on conditions, and for some scenarios, several paths might lead to a server call. Because of this, I need to know which method was called so I can have the correct asserts in my test (problem 2).

The variable serverCalled needs to be set to match a signature for the method call on the IServerAdapter mock. I would also very much like to be able to set that variable inside some callback or whatever which would let the logic of the code I'm testing flow the way it should (problem 1). Because of the way it works now, serverCalled will not be set by the mock setup.

The main problem is not knowing which method was called against the server. I've tried to use Match to check the name of the delegate method, but no sigar.

serverAdapterMock.Setup(c => c.CallServer(Match.Create<Action<IServerExposingToClient>>( x => IsAddResource(x)), out serverCalled)).Callback(() => someMethodCalled = true);

When IsAddResource is called, the function does not refer to AddResource, only where it was created and what argument it is called with.

Does anyone know how to check which method was called?

For the other problem with out serverCalled, you could argue that the void method could be bool instead, and that the other method could return null if we don't have a connection to the server (the last argument would be ambiguous as null could either mean object did not exist, or server was unavailable).

I would very much like suggestions for working out both problems.

Thanks in advance

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
bigfoot
  • 455
  • 5
  • 12
  • I have a feeling the reason I can't find out which method is being called is that the compiled code has the AddResource call inside an anonymous delegate which I (and perhaps also Moq) can't access the content of to find out what it does... – bigfoot Feb 24 '11 at 20:06

1 Answers1

6

Problem one:

As you've pointed out, a Callback here would be ideal. Unfortunately, because your method signature has a out parameter it currently cannot be intercepted by Moq. There's a post in the Moq forums that is similar to your concern. There's no indication that it will be addressed.

If you're open to changing your method signature, consider dropping the out parameter -- they're a bit of a smell anyway. Things would be a lot simpler if you just threw an exception when the server was not available. Getting that out of there (pun intended) would open your Callback to handle your second problem.

Problem two:

Consider changing your method signature to be Expression<Action<IServerExposingToClient>>. I realize there's a lot of angle brackets there, but an Expression is syntactically equivalent to Action<T> (you wouldn't have to change your calling code) and would allow your Callback to walk the code expression tree and get the name of your calling method.

You can get the name of the method using something like:

public string GetMethodName(Expression<Action<IServerExposingToClient>> exp)
{
    return ((ExpressionMethodCall)exp.Body).Method.Name;
}

You now have enough arsenal to go after your mock. You can either write a callback that logs the names of method calls or you can write matchers to help define behavior when methods are called.

For example:

[Test]
public void WhenTheViewModelIsLoaded_TheSystem_ShouldRecordOneNewResource()
{
   // arrange
   var serverAdapterMock = new Mock<IServerAdapter>();
   var subject = new SubjectUnderTest( serverAdapterMock.Object );

   // act
   subject.Initialize();

   // assert
   serverAdapterMock.Verify( c => c.CallServer( IsAddResource() ), Times.Exactly(1));
}

static Expression<Action<IServerExposedToClient>> IsAddResource()
{
    return Match.Create<Expression<Action<IServerExposedToClient>>>(
              exp => GetMethodName(exp) == "AddResource");
}
bryanbcook
  • 16,210
  • 2
  • 40
  • 69
  • 2
    +1. I agree; I wouldn't use an out parameter except in the Try-Parse Pattern. Exceptions, not error flags/codes, are the preferred way of communicating error conditions. – TrueWill Feb 25 '11 at 19:00
  • Problem two: Just tried it out, and it seems to work quite well. In my test, I changed the signature for the method call to accept an expression, and removed the out parameter and replaced it with a return value for the method accepting Action. I have to see what the project lead thinks. We then have to change previously well tested (not unit tests) code just before release. Specially since this change is design for testing, and not the other way arond. I'm not a fan of out parameters, and wouldn't choose it myself. – bigfoot Feb 26 '11 at 00:00
  • Problem one: I don't want the methods to throw exceptions. This would lead to loads of extra code for handling this. It is "prettier" (in lack of a better way to express myself) than try/catch blocks, and logging is performed inside of the CallServer method. – bigfoot Feb 26 '11 at 00:04
  • Is there a better way to work around this instead of changing the code? A different way of writing the unit tests to test for what method is called? Just need a workaround in case the change is not accepted. Is there any reason why this solution might cause problems? What could go wrong compared to my old approach? Performance penalties (though I don't think the user would notice)? – bigfoot Feb 26 '11 at 00:08
  • Perfect usage of Verify + Match. I was searching this for ages. – Custodio Jan 23 '12 at 13:18
  • @bigfoot: I know this is a late response to your question (11 months qualifies as long) but if you don't want to throw exceptions, change the return type of CallServer to return an object that contains both the return value and the exception if any. – bryanbcook Jan 23 '12 at 16:12