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.