Need Quality Code? Get Silver Backed

Data Access & Unit Testing

25thApr

0

by Gary H

In unit testing we want to be able to run all of our tests without needing to spin up any kind of backing data store - if you need a data store then you are running an integration test, not a unit test. As such we need to maintain separation of concerns between our business logic and our data layer. Today we will look at using the Repository pattern to achieve this.

What is it?

Let's start with a definition of what the pattern does:

[The repository pattern] Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

So.... what is it?

The repository pattern is an abstraction. It provides a layer on top of our data access which lets us ignore the implementation details of talking to our data source and allows us to concentrate instead on actually working with our data. Let's see an example, imagine we had a legacy system which we used to deal with invoices. Let's say we wanted to retrieve all of the invoices which were currently unpaid and for added realism, lets assume we are using LINQ to SQL. We may expect to see something like:

public class InvoiceReport()
{
	public void CreateUnpaidInvoiceReportData(XmlTextWriter xmlOut)
	{
		using (var db = new ExistingDB())
		{
			foreach (var i in db.Invoices.Where(i => i.Status == 3)
										 .ToList())
			{
				xmlOut.WriteStartElement("Invoice");
				xmlOut.WriteAttributeString("Number", i.Number);
				xmlOut.WriteEndElement();
			}
		}
	}
}

If we're really lucky we may even have an enum so that the status isn't using a magic number. Either way, this code is pretty horrid. We have a dependancy in our method that means that to call it we need to have a fully prepared database. Should we want to write a unit test for this code or for any code that calls this method anywhere in it's hierarchy we will need to have a database.

This isn't very clean and this is exactly the kind of problem that the repository pattern is here to prevent. Let's take a look at how this may be implemented using the repository pattern.

public class InvoiceAccessor()
{
	public IInvoiceRepository Invoices { get; protected set; }

	public InvoiceAccessor(IInvoiceRepository repository)
	{
		Invoices = repository;
	}

	public InvoiceAccessor() : this(new DefaultInvoiceRepository()) {}

	public void CreateUnpaidInvoiceReportData(XmlTextWriter xmlOut)
	{
		foreach (var i in Invoices.GetUnpaidInvoices())
		{
			xmlOut.WriteStartElement("Invoice");
			xmlOut.WriteAttributeString("Number", i.Number);
			xmlOut.WriteEndElement();
		}
	}
}

public class DefaultInvoiceRepository()
{
	public List<Invoice> GetUnpaidInvoices()
	{
		using (var db = new ExistingDB())
		{
			var unpaid = db.Invoices.Where(i => i.Status == 3).ToList();
		}
	} 
}

The pattern adds a little extra code in order to gain a lot of flexibility. By referencing the repository as an interface we immediately gain the ability to decouple it from the data source. By passing the repository into an optional constructor we give ourselves a way to override the default repository. The repository itself may well have code just like the first example but our business logic is now shielded from the implementation.

Should we want to create unit tests against this code we can pass in a mock invoice repository that has no database connection and instead acts on an in memory list or instead just returns fixed values. We can also change the implementation without impacting our business logic. Say the status should be 4 instead of 3 or we want to introduce an enum, we can do this in one place and know that everything that talks to the data source will be updated.

How should we use it?

The repository pattern works well with the principles of Inversion of Control and Dependency Injection. Our sample above is a very basic take on dependency injection as it is manually applied however it would be trivial to extend this to use any modern injection framework.

The key principle should always be separation of concerns. Hide the data access, expose meaningful methods with meaningful names and remove the data dependency from your code.

C# , Patterns

Comments are Locked for this Post