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
Comments
Anonymous
November 12, 2012
The comment has been removedAnonymous
November 21, 2012
There is already a thread social.msdn.microsoft.com/.../2ab9ef3e-2de2-40bc-871b-7cb42dbd2215 where I replied today. I replicated this issue in environment i.e. Win 8, VS 2010 & VS 2012. In order to drill down I created a new Unit Test project (new solution) and added reference to System.ServiceModel dll. I added Fakes assembly for System.ServiceModel reference. I ran the default unit test (empty test method) and it failed with same error message. I removed the System.ServiceModel.Fakes and instead added Fakes assembly for System. This time it worked. My initial thoughts are "Adding Fakes assembly for System.ServiceModel on Win 8 + VS 2010 + VS 2012 causes this error." I'll share if I find a workaround. Thanks.Anonymous
November 26, 2012
I have found a fix for this issue. The workaround is Go to Test> Test Settings>Default Processor Architecture> and change to X64. In my case it was X86. I changed it to X64, rebuild and ran the test ... it worked :) I earlier suspected that "Adding Fakes assembly for System.ServiceModel on Win 8 + VS 2010 + VS 2012 causes this error.". While that was true however it wasn’t a solution.