You can unit test all your layers, but when you get to the repository it becomes an exercise in mocking that provides little real benefit. That’s why we have to give up on the unit test idea for repositories and embrace integration tests. There’s nothing wrong with integration tests, as I explained a while back. So let’s review the criteria of effective automated tests and see if this is worth while.
- Automated – Yes, my tests will be standard NUnit tests that can be run just about anywhere by just about any automated process.
- Test the Program – For repository methods, I want to test each method against a real datasource so I can ensure my usage of the library is correct and that my schemas behave like I planned. I also want to make sure methods work together. For example, can I update using the same object I just read?
- Deterministic – This one is tricky, and is actually the whole reason for this post. With the proper methods, you can write deterministic integrated tests.
There’s one more criteria that I thought of after I wrote that article.
4. Concurrent – If at all possible, I would like my automated tests to be able to run concurrently. If two tests are running at the same time, they should not interfere with one another. This one is even more tricky than #3, but it can be done.
The first step is to make the repository more testable. The only real change I needed to do for this was to set up the repository to inject a properties object with the Azure DDB values: location, key, and database name. Sticking with good DI practice, I created an interface for the properties object, which is what the repository asks for. In practice the real object gets the values from ConfigurationManager (the app.config or web.config). For testing, I create a mock version and pass that in. Here’s the interface and mock implementation:
public interface IDocumentDBProperties
{
string Location { get; }
string Key { get; }
string DatabaseId { get; }
}
class MockDocumentDbProperties : IDocumentDBProperties
{
public string Location { get; set; }
public string Key { get; set; }
public string DatabaseId { get; set; }
}
The mock adds setters to the properties so I can set them in the test, like this:
[SetUp]
public void Setup()
{
_properties = new MockDocumentDbProperties
{
Location = ConfigurationManager.AppSettings["DocDbLocation"],
Key = ConfigurationManager.AppSettings["DocDbKey"],
DatabaseId = "dmart_test_" + Guid.NewGuid()
};
repo = new EquipmentDocDbRepository(_properties);
}
Because DDB has to be online (at least as of this writing), I need the same location and key from the app.config. But the new DatabaseId is what makes #3 and #4 possible. Each test will have its own database, which gets created at the beginning and deleted in the Teardown method, like so:
[TearDown]
public void TearDown()
{
var client = new DocumentClient(new Uri(_properties.Location),
_properties.Key);
var databaseLink = client
.GetOrCreateDatabaseAsync(_properties.DatabaseId)
.Result.SelfLink;
var deleteResult = client.DeleteDatabaseAsync(databaseLink)
.Result;
}
Now all that’s left is some actual tests. Usually, I like to write one generic test that hits all the methods for an integration test. Then, I’ll add more as necessary when bugs or sensitive code necessitate it. In this case, I add a record, get it by its ID, update it, delete it, and then get all to make sure it’s gone. It makes for one long test, but we are in integration test land and it still fits the criteria. This is a long code snippit, but not as long as I sometimes make them. Usually, I also put data in all the fields and assert that it goes in and out appropriately, but Equipment has a lot of fields so I left that out for brevity in this post.
[Test]
public async void Add_GetById_Update_Delete_GetAll()
{
// Add
var equipment = new Equipment
{
Name = "Add_GetById_Update_Delete_GetAll"
};
var addedEquipment = await repo.AddEquipmentAsync(equipment);
var newId = addedEquipment.id;
Assert.AreEqual("Add_GetById_Update_Delete_GetAll",
addedEquipment.Name);
// GetById
var retrievedEquipment = await repo.GetEquipmentByIdAsync(newId);
Assert.AreEqual("Add_GetById_Update_Delete_GetAll",
retrievedEquipment.Name);
// Update
retrievedEquipment.DamageSmall = "1d2";
var updatedEquipment = await repo.UpdateEquipmentAsync(newId,
retrievedEquipment);
Assert.AreEqual("Add_GetById_Update_Delete_GetAll",
updatedEquipment.Name);
Assert.AreEqual("1d2", updatedEquipment.DamageSmall);
// Delete
await repo.DeleteEquipmentAsync(newId);
// GetAll
var equipmentList = await repo.GetEquipmentsAsync();
Assert.AreEqual(0, equipmentList.Count());
}
I did end up adding another test when I was trying to get my code demo working for a presentation, but I’ll leave it as I think you probably get the idea by now.
I’ve also used this approach with Entity Framework code first entities. When I reach that stage in DungeonMart, I’ll cover how to do this in EF.
The code for this post is here: https://github.com/qanwi1970/dungeon-mart/tree/3cc755ea3b4a52d25d8bc5a7cc3feb1b6a670ada