Writing Tests for HealthVault Applications
We have added some useful new functionality designed to make it easier to test a HealthVault application.
The existing HealthVault SDKs didn’t make it easy if you wanted to write isolated (or method-level) unit tests for features that talked to the HealthVault Platform. You could usually do it by designing your own layer that could return simulated results (sometimes called “mocking” in agile methodologies), but that was sometimes difficult because of the way the SDK worked.
In this release, we’ve made some changes that should make this a whole lot easier.
For the sake of discussion, consider a bit of application code that fetches medications from HealthVault and filters them:
HealthRecordItemCollection GetNewMedications(HealthRecordAccessor record)
{
HealthRecordSearcher searcher = record.CreateSearcher(Medication.TypeId);
HealthRecordItemCollection items = searcher.GetMatchingItems()[0];
// filter items here...
return items;
}
We want to write a test that verifies that the “filter items here” part of the method works correctly, and we’d like the test to run without talking to the HealthVault platform. That’s not easy to do in the current SDK, because there’s no way to control what comes back from GetMatchingItems().
In the new SDK, there is a way to do that. If we debug down we will find that the call to GetMatchingItems ends up in the following method in a new class named HealthVaultPlatform.
public static ReadOnlyCollection <HealthRecordItemCollection > GetMatchingItems(
ApplicationConnection connection,
HealthRecordAccessor accessor,
HealthRecordSearcher searcher)
{
return HealthVaultPlatformItem .Current.GetMatchingItems(connection, accessor, searcher);
}
The HealthVaultPlatform class centralizes all operations (except for one exception I’ll cover later) in a single class – if the SDK needs to talk to HealthVault it goes through that class. You can call into that class directly if you wish, or just troll through to see what operations can be performed.
To create our test, we are going to be hooking in underneath that level. The method above just forwards into a method in the HealthVaultPlatformItem class, and that class provides a way for us to override the behavior.
To get started, we need to create a class that derives from HealthVaultPlatformItem and overrides the GetMatchingItems() method. The first version looks like this:
public class HealthVaultPlatformItemMock : HealthVaultPlatformItem
{
HealthRecordItemCollection _itemsToReturn;
public HealthVaultPlatformItemMock(params HealthRecordItem[] items)
{
_itemsToReturn = new HealthRecordItemCollection(items);
}
public override ReadOnlyCollection <HealthRecordItemCollection> GetMatchingItems(
ApplicationConnection connection,
HealthRecordAccessor accessor,
HealthRecordSearcher searcher)
{
List <HealthRecordItemCollection> collections =
new List <HealthRecordItemCollection>();
collections.Add(_itemsToReturn);
return new ReadOnlyCollection <HealthRecordItemCollection>(collections);
}
}
We can use it like this (this is an NUnit test):
[Test]
public void GetMatchingItems()
{
Medication medication = new Medication(new CodableValue("Ibuprofen" ));
Medication medication2 = new Medication(new CodableValue("Vitamin C" ));
HealthRecordItemCollection newItems = null ;
HealthVaultPlatformItemMock mock = new HealthVaultPlatformItemMock(medication, medication2);
HealthVaultPlatformItem.EnableMock(mock);
ApplicationConnection connection = new ApplicationConnection(Guid .NewGuid());
HealthRecordAccessor accessor = new HealthRecordAccessor(connection, Guid .NewGuid());
newItems = GetNewMedications(accessor);
HealthVaultPlatformItem.DisableMock(mock);
Assert.AreEqual(2, newItems.Count);
Assert.AreEqual("Ibuprofen" , ((Medication)newItems[0]).Name.Text);
Assert.AreEqual("Vitamin C" , ((Medication)newItems[1]).Name.Text);
}
When the call to GetMatchingItems() gets down to HealthVaultPlatformItems, it will end up calling our mocked method rather than the built-in one.
The code requires us to do a few things:
- Create an instance of the mock class.
- Enable the mock.
- Disable the mock.
We can make it nicer by having the mock class itself handle enabling and disabling the mock, using the following:
public class HealthVaultPlatformItemMock : HealthVaultPlatformItem, IDisposable
{
HealthRecordItemCollection _itemsToReturn;
public HealthVaultPlatformItemMock(params HealthRecordItem[] items)
{
_itemsToReturn = new HealthRecordItemCollection(items);
HealthVaultPlatformItem.EnableMock(this );
}
public override ReadOnlyCollection <HealthRecordItemCollection> GetMatchingItems(
ApplicationConnection connection,
HealthRecordAccessor accessor,
HealthRecordSearcher searcher)
{
List <HealthRecordItemCollection> collections = new List <HealthRecordItemCollection>();
collections.Add(_itemsToReturn);
return new ReadOnlyCollection <HealthRecordItemCollection>(collections);
}
#region IDisposable
~HealthVaultPlatformItemMock()
{
Dispose(false );
}
/// <summary>
/// Disposes the request.
/// </summary>
///
public void Dispose()
{
Dispose(true );
GC.SuppressFinalize(this );
}
/// <summary>
/// Disables the mocking.
/// </summary>
///
/// <param name="disposing"></param>
///
protected void Dispose(bool disposing)
{
HealthVaultPlatformItem.DisableMock();
}
#endregion IDisposable
}
That allows us to simplify our test code to this:
[Test]
public void GetMatchingItems()
{
Medication medication = new Medication(new CodableValue("Ibuprofen" ));
Medication medication2 = new Medication(new CodableValue("Vitamin C" ));
HealthRecordItemCollection newItems = null ;
using (HealthVaultPlatformItemMock mock = new HealthVaultPlatformItemMock(medication, medication2))
{
ApplicationConnection connection = new ApplicationConnection(Guid .NewGuid());
HealthRecordAccessor accessor = new HealthRecordAccessor(connection, Guid .NewGuid());
newItems = GetNewMedications(accessor);
}
Assert.AreEqual(2, newItems.Count);
Assert.AreEqual("Ibuprofen" , ((Medication) newItems[0]).Name.Text);
Assert.AreEqual("Vitamin C" , ((Medication) newItems[1]).Name.Text);
}
Special Classes
There are a few classes where it’s not straightforward to create the class. For classes such as ServiceInfo, we don’t provide a way to create and modify them directly. To create an instance of those classes, you need to derive a new class and use that:
public class ServiceInfoTest : ServiceInfo
{
public ServiceInfoTest(string version)
{
Version = version;
}
}
and the associated test uses this class instead of ServiceInfo:
[Test]
public void GetServiceInfoTest()
{
ApplicationConnection connection = new ApplicationConnection(Guid .NewGuid());
ServiceInfoTest serviceInfo = new ServiceInfoTest("V2.x" );
ServiceInfo serviceInfoBack = null ;
using (HealthVaultPlatformInformationMock mock = new HealthVaultPlatformInformationMock(serviceInfo))
{
serviceInfoBack = connection.GetServiceDefinition();
}
Assert.AreEqual("V2.x" , serviceInfoBack.Version);
}
Limitations
We currently don’t have mockable interfaces for blob operations. We hope to do that in a future release.