Unit Test a project having external dependency(WCF Proxy) using Fakes & Visual Studio 11 Beta
In this post I’ll explain the steps to generate unit tests for a project which calls a WCF service using Fakes. Microsoft Fakes is an isolation framework for creating delegate-based test stubs and shims in .NET Framework applications. The Fakes framework can be used to shim any .NET method, including non-virtual and static methods in sealed types.
The Fakes framework helps developers create, maintain, and inject dummy implementations in their unit tests. The Fakes framework generates and maintains stub types and shim types for a system under test.
I had previously discussed about creating Unit Tests using Pex, Moles and Visual Studio 2010. The projects inside the sample solution are
- DemoService: This project is a WCF Service.
- DemoLibrary: This project is a Class library and service reference to DemoService has been added. Unit tests will be generated for this project.
- ConsoleApp: This project is a Console application.
- DemoLibrary.Tests: This is a Test project and contains unit tests for DemoLibrary.
The solution structure is displayed below
DemoLibrary calls DemoService though proxy as displayed in the Layer diagram
I’ll now discuss in brief the code snippets of each project
WCF Service(DemoService): This service provides only a single operation
[ServiceContract]
public interface IDemoService
{
[OperationContract]
string Search(string criteria);
}
WCF Service Client(DemoLibrary): It calls the Search method of DemoService through proxy as displayed below
public string GetResults(string s)
{
DemoServiceReference.DemoServiceClient client = null;
try
{
client = new DemoServiceReference.DemoServiceClient();
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ChannelFactory.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
s = client.Search(s);
return s;
}
finally
{
if (client != null)
{
if (client.State == CommunicationState.Opened)
{
client.Close();
}
else if (client.State == CommunicationState.Faulted)
{
client.Abort();
}
}
}
}
Adding Unit Tests for DemoLibrary:
In order to Unit Test WCF Service Client(DemoLibrary) project using Fakes and Visual Studio 11 the steps are
Add a new Unit Test project as displayed below
In order to isolate the dependencies we need to add Fakes for DemoLibrary and System.ServiceModel assemblies and behaviour will be redefined using delegates. Add Fakes assembly as displayed below
A .fakes file will be added to the project under Fakes folder and reference to {Assembly}.Fakes.dll will be added as displayed below
Similarly as explained above we need to generate Fakes for System.ServiceModel assembly. I got “Failed to generate Stub/Shim for type …” error messages on building the project. There are couple of ways to fix it.
The next step is to Mock the Service call(redefine behaviour using delegates) and add Asserts as displayed in code snippets below. The main points are
- I have used Shims to isolate calls to non-virtual functions in unit test methods. Read more about Shim and Stub types.
- When using shim types in a unit test framework, you must wrap the test code in a ShimsContext to control the lifetime of your shims.
[TestMethod] public void TestSearch() { using (ShimsContext.Create()) { ShimWCFService<IDemoService>(); ShimDemoServiceClient.Constructor = (var1) => { new ShimDemoServiceClient { }; }; ShimDemoServiceClient.AllInstances.SearchString = (var1, var2) => { return "Result"; }; Search search = new Search(); string result = search.GetResults("test"); Assert.IsNotNull(result); Assert.AreEqual(result, "Result"); } } /// <summary> /// Mocks the WCF service. /// </summary> private void ShimWCFService<TService>() where TService : class { ShimClientCredentials.Constructor = (var1) => { new ShimClientCredentials(); }; ShimClientCredentials.AllInstances.WindowsGet = (var1) => { return new ShimWindowsClientCredential(); }; ShimWindowsClientCredential.AllInstances.ClientCredentialGet = (var1) => { return new System.Net.NetworkCredential(); }; ShimWindowsClientCredential.AllInstances.ClientCredentialSetNetworkCredential = (var1, var2) => { }; ShimWindowsClientCredential.AllInstances.AllowNtlmGet = (var1) => { return true; }; ShimWindowsClientCredential.AllInstances.AllowNtlmSetBoolean = (var1, var2) => { }; ShimWindowsClientCredential.AllInstances.AllowedImpersonationLevelGet = (var1) => { return System.Security.Principal.TokenImpersonationLevel.Impersonation; }; ShimWindowsClientCredential.AllInstances.AllowedImpersonationLevelSetTokenImpersonationLevel = (var1, var2) => { }; ShimChannelFactory.AllInstances.CredentialsGet = (var1) => { return new ShimClientCredentials(); }; ShimClientBase<TService>.AllInstances.ClientCredentialsGet = (var1) => { return new System.ServiceModel.Description.ClientCredentials(); }; ShimClientBase<TService>.AllInstances.ChannelFactoryGet = (var1) => { return new ShimChannelFactory<TService>(); }; ShimClientBase<TService>.AllInstances.StateGet = (var1) => { return CommunicationState.Opened; }; ShimClientBase<TService>.AllInstances.Close = (var1) => { }; ShimClientBase<TService>.AllInstances.Abort = (var1) => { }; }
Go to Unit Test explorer and run the tests
You can also Analyze the code coverage. The code coverage results are displayed below
The sample is available for download @ Download Source code