Test async webservice call with a Task

We have a number of services in our project. Our clients include WPF, iOS, Android and Web. To have the best interoperability we communicate over http and expose WSDL for proxy generation. This means we cannot change the contracts or introduce new versions when we break compatibility. We plan to test the compatibility of our changes with unit tests.

The unit tests host services based on the latest contract. Then every released proxy is used to call the known operations. When the call succeeds, the results and parameters are validated on both ends. This way we know what is broken.

To test the async calls to the services I’m using a Task. See the code below, other methods are left out for clarity.

[TestMethod]
public void Print_async()
{
    // Arrange
    var mock = GenerateServiceMock();
    mock.Expect(x => x.Print(null)).IgnoreArguments().Return(1)
        .WhenCalled((x) => Thread.Sleep(3000)); // included to simulate lag

    // Act
    var actual = default(int);
    using (var host = StartService(mock))
    {
        var proxy = GenerateProxy();
        // call print direct
        // var actual = proxy.Print("Hello World");        
        // call print async
        var async = proxy.BeginPrint("Hello World", null, null);
        // task to handle async
        var task = Task.Factory.FromAsync(async, (x) => actual = proxy.EndPrint(x));
        // wait for it
        task.Wait();
        // cleanup
        host.Close();
    }

    // Assert
    Assert.AreEqual<int>(1, actual);
    var args = mock.GetArgumentsForCallsMadeOn(x => x.Print(null));
    mock.VerifyAllExpectations();
    Assert.AreEqual("Hello World", args[0][0]);
}

Because our project is on the 4.0 version of the dotnet framework we can’t use async/await. The FromAsync method takes the IAsyncResult from the BeginPrint call and waits for it to be completed. When it completes the EndPrint method is called to receive the result from the server. Until the task we’ve created finishes the execution is put on hold with task.Wait().

The code for testing the service direct or async only differs in the call of the operation. The Arrange and Assert sections are untouched.

Complete sample below

// you'll  need rhino mocks: http://hibernatingrhinos.com/downloads/rhino-mocks/latest
[ServiceContract]
public interface IPrintable
{
    [OperationContract]
    int Print(string document);

    [OperationContract (AsyncPattern = true)]
    IAsyncResult BeginPrint(string document, AsyncCallback callback, object state);

    int EndPrint(IAsyncResult async);
}

private IPrintable GenerateServiceMock()
{
    Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add<ServiceContractAttribute>();
    var mock = MockRepository.GenerateStrictMock<IPrintable>();
    return mock;
}
private ServiceHost StartService(IPrintable mock)
{
    var service = new ServiceHost(mock, new Uri("http://localhost:1233/printservice"));
    // ! enable hosting an instance (not a type)
    service.Description.Behaviors.Find<ServiceBehaviorAttribute>().InstanceContextMode = 
        InstanceContextMode.Single;
    service.Open();
    return service;
}
private IPrintable GenerateProxy()
{
    // abc for proxy
    var address = new EndpointAddress("http://localhost:1233/printservice");
    var binding = new BasicHttpBinding();
    var channel = ChannelFactory<IPrintable>.CreateChannel(binding, address);
    return channel;
}

[TestMethod]
public void Print()
{
    // Arrange
    var mock = GenerateServiceMock();
    mock.Expect(x => x.Print(null)).IgnoreArguments().Return(1);

    // Act
    var actual = default(int);
    using (var host = StartService(mock))
    {
        // abc for proxy
        var proxy = GenerateProxy();
        // call print async
        actual = proxy.Print("Hello World");
        // cleanup
        host.Close();
    }

    // Assert
    Assert.AreEqual<int>(1, actual);
    var args = mock.GetArgumentsForCallsMadeOn(x => x.Print(null));
    mock.VerifyAllExpectations();
    Assert.AreEqual("Hello World", args[0][0]);
}

[TestMethod]
public void Print_async()
{
    // Arrange
    var mock = GenerateServiceMock();
    mock.Expect(x => x.Print(null)).IgnoreArguments().Return(1)
        .WhenCalled((x) => System.Threading.Thread.Sleep(3000));

    // Act
    var actual = default(int);
    using (var host = StartService(mock))
    {
        // abc for proxy
        var proxy = GenerateProxy();
        // call print async
        var async = proxy.BeginPrint("Hello World", null, null);
        // wait for it
        var task = Task.Factory.FromAsync(async, (x) => actual = proxy.EndPrint(x));
        task.Wait();
        // cleanup
        host.Close();
    }

    // Assert
    Assert.AreEqual<int>(1, actual);
    var args = mock.GetArgumentsForCallsMadeOn(x => x.Print(null));
    mock.VerifyAllExpectations();
    Assert.AreEqual("Hello World", args[0][0]);
}

About erictummers

Working in a DevOps team is the best thing that happened to me. I like challenges and sharing the solutions with others. On my blog I’ll mostly post about my work, but expect an occasional home project, productivity tip and tooling review.
This entry was posted in Test and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.