Windows Azure SDK better with wrappers

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.

About erictummers

My work as a recruited developer changes almost every month. I like challenges and sharing the solutions with others. On my blog I’ll mostly post about my work, but expect an occasional home project, productivity tip and tooling review.
This entry was posted in Development and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s