Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Asynchronous Calls in Silverlight WCF Proxy using Moq

I have a typical Silverlight application with a WCF service and I am using slsvcutil.exe to generate the standard client proxy to communicate with the web service. I am trying to write unit tests and I'm attempting to use the Silverlight Unit Testing framework and Moq to mock the proxy and remove the service dependency for testing.

I am very new to Moq and having a lot of trouble automatically raising the various Completed events on the mocked proxy automatically when service calls are made to simulate the async calls.

In order to make the proxy "mockable" I've created my own simple interface for the generated proxy calls and their completed events:

public interface IServiceProxy
{
    void TestAsync(TestRequest request);
    void TestAsync(TestRequest request, object userState);
    event EventHandler<TestCompletedEventArgs> TestCompleted;
}

I also subclassed the generated proxy object to implement that interface:

public class MyServiceProxy : GeneratedServiceClient, IServiceProxy, ICommunicationObject
{
    // ... overloaded proxy constructors
}

After looking at the Moq documentation, this is how I am attempting to set up the mock to expect the TestAsync() call and immediately raise the TestCompleted event with the result in the EventArgs:

[TestMethod]
public void Test_Returns_Expected()
{
    var mockProxy = new Mock<IServiceProxy>();
    var result = new TestResponse() { Value = true };

    this.mockProxy.Setup(
        p => p.TestAsync(It.IsAny<TestRequest>()))
        .Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, null));

    // rest of the test to actually use the mock and assert things
}

Everything builds fine, but when I attempt to run any kind of test using the mock and set break points the TestCompleted event is never being raised when I call TestAsync().

Is there anything obvious that I am missing or any better ideas about mocking these types of async service proxies in Silverlight?

Thanks!

EDIT:

To be more clear what I am actually trying to test is a helper class I made which takes an instance of IServiceProxy and provides a cleaner service interface for my ViewModel to use by accepting Action<TResponse, Exception> callback parameters rather than dealing with callback events in my ViewModel. I understand how I could mock this as well to directly test my ViewModel but I figured it would be nice to test the helper class by itself first.

Here is an example of what I am talking about:

public class HelperClient : IServiceHelper
{
    private IServiceProxy proxy;

    public HelperClient(IServiceProxy proxy)
    {
        this.proxy = proxy;

        // register to handle all async callback events
        this.proxy.TestCompleted += new EventHandler<TestCompletedEventArgs>(TestCompleted);
    }

    public void Test(TestRequest request, Action<TestResponse, Exception> response)
    {
        this.proxy.TestAsync(request, response);
    }

    private void TestCompleted(object sender, TestCompletedEventArgs e)
    {
        var response = e.UserState as Action<TestResponse, Exception>;

        if (response != null)
        {
            var ex = GetServiceException(e);

            if (ex == null)
            {
                response(e.Result, null);
            }
            else
            {
                response(null, ex);
            }
        }
    }
}

So in my test what I am really doing is mocking ISerivceProxy and passing it in and just attempting to test a service call to make sure the wrapper it invoking the Action correctly:

[TestMethod]
[Asynchronous]
public void Test_Returns_Expected()
{
    var mockProxy = new Mock<IServiceProxy>();
    var helper = new HelperClient(mockProxy.Object);
    bool expectedResult = true;
    var result = new TestResponse() { Value = expectedResult };

    this.mockProxy.Setup(
        p => p.TestAsync(It.IsAny<TestRequest>()))
        .Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, null));

    helper.Test(new TestRequest(), (response, ex) =>
    {
        Assert.AreEqual(expectedResult, response.Value);
        EnqueueTestComplete();
    });
}

The problem is that the mocked proxy object is never raising the TestCompleted event so my response action is never getting invoked to finish the test (even though the test appears to complete successfully the Assert is never actually run). Sorry for such a long post, just trying to show you as much code as possible.

EDIT 2

Added [Asynchronous] and call to EnqueueTestComplete() which I realized I may need to make the test wait for the event to be raised. This did not really help, the event is still never raised so the test just hangs and never completes.

EDIT 3

Aliostad's answer was correct that my setup expectation's signature did not match the actual Test() signature allowing me to pass in a response Action as the second param. Stupid mistake, but that is what was preventing Moq from raising the Completed event. I was also forgetting to pass the Action in as the userState object in the TestCompletedEventArgs so that it would actually be invoked when the Completed event was raised. Also, the [Asynchronous] and EnqueueTestCompleted did not seem to be necessary in this case.

Here is updated test code for anyone interested:

[TestMethod]
public void Test_Returns_Expected()
{
    var mockProxy = new Mock<IServiceProxy>();
    var helper = new HelperClient(mockProxy.Object);
    bool expectedResult = true;
    var result = new TestResponse() { Value = expectedResult };

    Action<TestResponse, Exception> responseAction = (response, ex) =>
    {
        Assert.AreEqual(expectedResult, response.Value);
    };

    this.mockProxy.Setup(
        p => p.TestAsync(It.IsAny<TestRequest>(), It.IsAny<Action<TestResponse, Exception>>()))
        .Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, responseAction));

    helper.Test(new TestRequest(), responseAction);
}
like image 511
Dan Auclair Avatar asked Oct 01 '10 17:10

Dan Auclair


1 Answers

Mocking events is quite a pain and unit tests become brittle. But as you said there is no way around it. But normally you would make the call you are trying to test and block the current thread (using Sleep or other methods) until event is raised (or a time-out).

It is actually not clear what you are testing. I can see a mock and a response, where is the actual real object?

I will update my answer accordingly.

UPDATE

I can see a problem here:

helper.Test(new TestRequest(), (response, ex) =>
{
    Assert.AreEqual(expectedResult, response.Value);
    EnqueueTestComplete();
});

in the last statement, you are putting EnqueueTestComplete(); and you assert but this action will never be used because it is passed to the moq object.

Also you are setting the expectation for TestAsync(It.IsAny<TestRequest>())) (one argument) while you are calling it with two arguments in the HelperClient (this.proxy.TestAsync(request, response);) and that is why it never gets fired since expectation is not met.

like image 184
Aliostad Avatar answered Oct 20 '22 11:10

Aliostad