Home Unit-test WCF contracts match for sync / async?
Reply: 2

Unit-test WCF contracts match for sync / async?

Scott Wegner
1#
Scott Wegner Published in 2014-01-02 16:36:46Z

WCF makes it easy to call services synchronously or asynchronously, regardless of how the service is implemented. To accommodate clients using ChannelFactory, services can even define separate sync/async contract interfaces. For example:

public interface IFooService
{
    int Bar();
}

[ServiceContract(Name = "IFooService")]
public interface IAsyncFooService
{
    Task<int> BarAsync();
}

This allows the client to reference either contract version, and WCF translates the actual API calls automatically.

One drawback to providing both contract versions is that they must be kept in-sync. If you forget to update one, the client may receive a contract mismatch exception at runtime.

Is there an easy way to unit test the interfaces to ensure they match from a WCF metadata perspective?

Scott Wegner
2#
Scott Wegner Reply to 2014-01-08 18:59:46Z

You can retrieve the ContractDescription and use WsdlExporter to generate the WSDL. The output MetadataSet is XML serializable, so you can compare the representations for each contract version to ensure they match:

    [TestMethod]
    public void ContractsMatch()
    {
        // Arrange
        string expectedWsdl = this.GetContractString<IFooService>();

        // Act
        string actualWsdl = this.GetContractString<IAsyncFooService>();

        // Assert
        Assert.AreEqual(expectedWsdl, actualWsdl);
    }

    private string GetContractString<TContract>()
    {
        ContractDescription description = ContractDescription.GetContract(typeof(TContract));
        WsdlExporter wsdlExporter = new WsdlExporter();

        wsdlExporter.ExportContract(description);
        if (wsdlExporter.Errors.Any())
        {
            throw new InvalidOperationException(string.Format("Failed to export WSDL: {0}", string.Join(", ", wsdlExporter.Errors.Select(e => e.Message))));
        }

        MetadataSet wsdlMetadata = wsdlExporter.GetGeneratedMetadata();

        string contractStr;
        StringBuilder stringBuilder = new StringBuilder();
        using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
        {
            wsdlMetadata.WriteTo(xmlWriter);
            contractStr = stringBuilder.ToString();
        }

        return contractStr;
    }
chiccodoro
3#
chiccodoro Reply to 2014-01-15 10:00:29Z

Your own answer is great. As BartoszKP points out it is more of an integration test, but this might be the best fit. You could argue that comparing two units (interfaces) to each other is not a unit test by definition.

The advantage of your approach is that you can be sure to verify what WCF makes from your classes. If you only want to test your own code, you could do something like that:

[TestMethod]
public void ContractsMatch()
{
    var asyncMethodsTransformed = typeof(IAsyncFooService)
        .GetMethods()
        .Select(mi => new 
        { 
            ReturnType = mi.ReturnType,
            Name = mi.Name,
            Parameters = mi.GetParameters()
        });
    var syncMethodsTransformed = typeof(IFooService)
        .GetMethods()
        .Select(mi => new
        {
            ReturnType = WrapInTask(mi.ReturnType),
            Name = Asyncify(mi.Name),
            Parameters = mi.GetParameters()
        });

    Assert.That(asyncMethodsTransformed, Is.EquivalentTo(syncMethodsTransformed));
}

The idea is that for each method in your IFooService you expect a method which has a similar signature with clearly defined transformations:

  • The name must contain a "Async" after the "I"
  • The return type must be a Task of the type found in the sync version.

The WrapInTask and Asyncify are left as exercise :-) If you like this suggestion I can expand on them.

By using a test like that you might constrain the code more than WCF does (I don't know the Async support very well). But even if it does you might want that to ensure some code consistency.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.316334 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO