Since Windows Azure SDK 1.2 back in 2010 I’m trying to get my unittests up and running. I posted about it on the Microsoft forum and consulted the JustMock forum. Nobody was able to provide the solution as it looks like a loophole in C#. Last week I came across AzureContrib project on codeplex and the project from Noopman gave me a good workaround: use wrappers.
Wrappers are used to decouple from the Microsoft assemblies and to introduce Interfaces for easy unittesting. This also gives me the opportunity to do a little trick with static operations and a singleton. See code below.
// My RoleEnvironment wrapper public class RoleEnvironmentHelper { // Initializes the RoleEnvironmentHelper class on first (static) use. static RoleEnvironmentHelper() { // singleton implementation Implementation = new RoleEnvironmentHelperImplementation(); } // The implementation which can be replaced by a Mock. public static RoleEnvironmentHelperImplementation Implementation { get; set; } // Gets the local resource from RoleEnvironment. public static ILocalResource GetLocalResource(string nameOfLocalResource) { return Implementation.GetLocalResource(nameOfLocalResource); } } // My implementation class public class RoleEnvironmentHelperImplementation { public virtual ILocalResource GetLocalResource(string nameOfLocalResource) { // use the Microsoft Windows Azure SDK RoleEnvironment to do the real work var resource = RoleEnvironment.GetLocalResource(nameOfLocalResource); // wrap the resource to use our Interface return new LocalResourceWrapper(resource); } // My LocalResource wrapper // internal class because the Interface is used for usage outside this implementation class internal class LocalResourceWrapper : ILocalResource { // Initializes the LocalResourceWrapper class public LocalResourceWrapper(LocalResource resource) { Imp = resource; } // All properties are delegated to this implementation private LocalResource Imp { get; set; } // ILocalResource implementation public int MaximumSizeInMegabytes { get { return Imp.MaximumSizeInMegabytes; } } public string Name { get { return Imp.Name; } } public string RootPath { get { return Imp.RootPath; } } } } // ILocalResource for completeness public interface ILocalResource { // These are the public properties from the LocalResource abstract class int MaximumSizeInMegabytes { get; } string Name { get; } string RootPath { get; } }
The RoleEnvironmentHelper removes the need for Windows Azure SDK assembly references in my main projects when it lives in its own WindowsAzure.Util assembly. Also I can replace the Implementation by a Mock object that only has to return a Mock/Stub of type ILocalResource. No more compiler errors, no more problems upgrading the SDK assemblies and the possibility to move to another platform. Win-win-win I would say. A sample unittest for JustMock, Rhino Mocks and Microsoft Moles below
[TestMethod] public void GetLocalResource_Name_with_telerik_justmock_implementation() { // Arrange var stub = Mock.Create<ILocalResource>(); Mock.Arrange(() => stub.Name).Returns("some_resource"); var mock = Mock.Create<RoleEnvironmentHelperImplementation>(Behavior.Strict); // GetLocalResource should return our stub mock.Arrange((ILocalResource) => mock.GetLocalResource("some_resource")).Returns(stub); // Use our mock for the RoleEnvironment implementation RoleEnvironmentHelper.Implementation = mock; // Act var result = RoleEnvironmentHelper.GetLocalResource("some_resource"); // Assert Assert.IsNotNull(result); Assert.AreEqual<string>("some_resource", result.Name); } [TestMethod] public void GetLocalResource_RootPath_with_rhino_mock_implementation() { // Arrange var repo = new MockRepository(); var stub = repo.Stub<ILocalResource>(); SetupResult.For(stub.RootPath).Return(@"c:\temp"); var mock = repo.StrictMock<RoleEnvironmentHelperImplementation>(); // GetLocalResource should return our stub mock.Expect((ILocalResource) => mock.GetLocalResource("some_resource")).Return(stub); // Use our mock for the RoleEnvironment implementation RoleEnvironmentHelper.Implementation = mock; repo.ReplayAll(); // Act var result = RoleEnvironmentHelper.GetLocalResource("some_resource"); // Assert Assert.IsNotNull(result); Assert.AreEqual<string>(@"c:\temp", result.RootPath); } [TestMethod] [HostType("Moles")] public void GetLocalResource_MaximumSize_with_Moles_implementation() { // Arrange var stub = new SILocalResource(); stub.MaximumSizeInMegabytesGet = () => { return 1024; }; var mock = new MRoleEnvironmentHelperImplementation(); // GetLocalResource should return our stub mock.GetLocalResourceString = (input) => { return stub; }; // Use our mock for the RoleEnvironment implementation RoleEnvironmentHelper.Implementation = mock; // Act var result = RoleEnvironmentHelper.GetLocalResource("some_resource"); // Assert Assert.IsNotNull(result); Assert.AreEqual<int>(1024, result.MaximumSizeInMegabytes); }
Microsoft Moles is available in Visual Studio 11 (beta) with the name Fakes.