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?
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;
}
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 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With