Windows Azure and Object-Oriented Testable Code
I am currently working on an Azure application that makes heavy use of blob storage (think raw data storage), table storage (think object database) and queues (think queues). From a high level point of view, these are constructs that could be useful to many enterprise applications being built today.
With that said, I’ve had to do a little work to make Azure more friendly to my way of building software. Let me deviate for a moment…
Lately, I have tried to distill everything that I know about object-oriented programming down to one statement: If I can test it, it’s good enough for now. There’s code readability and things like that which must be considered, but when I say this I am really referring to the structural elements of the application. Good enough for now means that everything is in a state that can be easily refactored later (if need be) and it means that the code is testable in an automated way (NUnit). My designs tend to rely on dependency injection, inversion of control and make heavy use of abstractions.
The Azure Storage API (what I’m mostly familiar with in the world of Azure) seems to be a throwback to older MS designs: sealed classes based on no abstractions (interfaces, abstract base classes). In other words, it’s an all or nothing black box approach. So forget about mocking. Granted, MS makes stuff easy to use but at the same time doesn’t seem to understand that that how I connect everything together is important, too.
Fortunately, it has been trivial to get around this using Bridge/Adapter-ish patterns.
My approach to blob storage has been something along these lines (THIS IS NOT PRODUCTION READY CODE – EXAMPLE ONLY):
public interface IBlob
{
void UploadFile(string fileName);
// just a few more methods here
// not exposing every method on CloudBlob
}
public interface IBlobContainer
{
IBlob GetBlobReference(string blobAddress);
}
public interface IBlobStorage
{
IBlobContainer GetContainer(string name);
}
public class BlobStorage : IBlobStorage
{
private readonly CloudBlobClient _client;
public BlobStorage()
{
var account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
_client = account.CreateCloudBlobClient();
}
public IBlobContainer GetContainer(string name)
{
return new BlobContainer(_client, name);
}
}
public class BlobContainer : IBlobContainer
{
private readonly CloudBlobClient _client;
private readonly string _containerName;
public BlobContainer(CloudBlobClient client, string containerName)
{
_client = client;
_containerName = containerName;
}
public IBlob GetBlobReference(string blobAddress)
{
var container = _client.GetContainerReference(_containerName);
container.CreateIfNotExist();
var blob = container.GetBlobReference(blobAddress);
return new Blob(blob);
}
}
public class Blob : IBlob
{
private readonly CloudBlob _cloudBlob;
public Blob(CloudBlob cloudBlob)
{
_cloudBlob = cloudBlob;
}
public void UploadFile(string fileName)
{
_cloudBlob.UploadFile(fileName);
}
}
This gives me IBlob, IBlobContainer and IBlobStorage….my own interfaces, which can easily be mocked and improves my testability.
I am doing something similar with queues. Again, this is an example only (I say this over and over in an attempt to keep “nitpicking” type comments to a minimum). Rest assured, my production code is a little more rich than this.
public interface IQueueMessage
{
object Content { get; }
string AsString { get; }
}
public interface IQueue
{
void AddMessage(IQueueMessage message);
IQueueMessage GetMessage();
void DeleteMessage(IQueueMessage message);
}
public interface IQueueLocator
{
IQueue GetQueue(string queueName);
}
public class QueueMessage : IQueueMessage
{
internal CloudQueueMessage CloudQueueMessage { get; private set; }
public QueueMessage(CloudQueueMessage cloudQueueMessage)
{
CloudQueueMessage = cloudQueueMessage;
}
public QueueMessage(byte[] content)
{
CloudQueueMessage = new CloudQueueMessage(content);
}
public QueueMessage(string content)
{
CloudQueueMessage = new CloudQueueMessage(content);
}
public object Content
{
get { return CloudQueueMessage; }
}
public string AsString
{
get { return CloudQueueMessage.AsString; }
}
}
public class Queue : IQueue
{
private readonly CloudQueue _cloudQueue;
public Queue(CloudQueue cloudQueue)
{
_cloudQueue = cloudQueue;
}
public void AddMessage(IQueueMessage message)
{
if (_cloudQueue == null)
return;
_cloudQueue.AddMessage((CloudQueueMessage)message.Content);
}
public IQueueMessage GetMessage()
{
if (_cloudQueue != null)
{
var content = _cloudQueue.GetMessage();
return new QueueMessage(content);
}
return null;
}
public void DeleteMessage(IQueueMessage message)
{
if (_cloudQueue != null)
{
var content = (CloudQueueMessage)message.Content;
_cloudQueue.DeleteMessage(content);
}
}
}
public class QueueLocator : IQueueLocator
{
private static CloudQueueClient _queueStorage;
public IQueue GetQueue(string queueName)
{
if (_queueStorage == null)
{
CreateQueueStorage();
}
if (_queueStorage != null)
{
var cloudQueue = _queueStorage.GetQueueReference(queueName);
cloudQueue.CreateIfNotExist();
return new Queue(cloudQueue);
}
return null;
}
private static void CreateQueueStorage()
{
var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
_queueStorage = storageAccount.CreateCloudQueueClient();
}
}
Conclusion
Not perfect, I know, but it’s good enough for now. I have hidden the fact that there are CloudStorageAccount’s and CloudBlobClient’s and CloudQueueClient’s. This may or may not work for you. My main concern is that I have something that I can mock. I can use dependency injection and inversion of control and I am able to see how my application interacts with Azure in my unit tests.

