comments (not for humans)
Datasets tend to make applications really hard to test. In this entry I'll explain a method you can use to improve the testability of your dataset-driven architecture.

Goal
Allow developers to write unit tests for classes that are using datasets to fetch values from databases (or other sources).

The idea
By using interfaces instead of the actual implementation, we can remove the dependencies on the datasets. Classes using the dataset can now be tested without touching or filling the dataset.

The scenario
We have a dataset called TdsCompany which contains our employees (see the image on the right). We have a web service which we can use to get updated address information for our employees. We can get a list of the SSNs of the employees that have been updated since a given date, and we can then fetch the addresses we want and we can use this data to update our employee database. We want to run this as a task, and we want to be able to test the functionality of this task without putting fake data in the dataset (which is cumbersome), invoking the database with our employees (which is slow) or invoking the web service (which is extremely slow). Unit tests need to be fast. If not the developers won't run them. And our tests will be very fragile if we don't control our test data.

A failed approach
Our first approach is to just create a reference to the dataset and web service in the UpdateAddressTask constructor and then go for it. This is however very difficult to test:
public UpdateAddressTask()
{
_empService = new TdsCompany();
_adrService = new AddressWebService();
}

public void Run()
{
List ssnList = _adrService.GetListOfUpdates(DateTime.Now.AddDays(-10));
foreach(string ssn in ssnList)
{
TdsCompany.EmployeeRow employee = _empService.GetEmployee(ssn);
Address address = _adrService.GetAddressOf(ssn);
...


A slightly better approach
If we pull the creation of the dataset into a separate contructor, we can at least fill the dataset ourselves:
public UpdateAddressTask() : this(new TdsCompany())
{}

public UpdateAddressTask(TdsCompany dataset)
{
_empService = dataset;
_adrService = new AddressWebService();
}

public void Run()...


A better solution
We first create an interface for the webservice and pull that out into the constructor as well. We also create an interface for our
dataset that we now refer to as an employee service:
public interface IAddressService
{
List GetListOfUpdates(DateTime since);
Address GetAddressOf(string ssn);
}

public interface IEmployeeService
{
IEmployee GetEmployee(String ssn);
}

private IEmployeeService _empService;
private IAddressService _adrService;

public UpdateAddressTask() : this(new TdsCompany(), new AddressWebService()) {}

public UpdateAddressTask(IEmployeeService empService, IAddressService adrService)
{
_empService = empService;
_adrService = adrService;
}
We could of course have used Spring.NET dependency injection instead of creating our services in the extra constructor.

The next thing we have to do is to make sure both the data returned from the web service and from the employee service is generic. We don't want our tests or client classes to be dependent on the dataset. Our address service used a simple Address struct, so we'll leave that alone. Our dataset however returned an TdsCompany.EmployeeRow, and this sounds very dataset-ish to me. If you look at the interface description for IEmployeeService above, you'll see that I've cheated and allready introduced an IEmployee interface:
public interface IEmployee
{
string SSN { get; }
string FirstName { get; }
string LastName { get; }
string Street { get; set; }
string Town { get; set; }
string ZipCode { get; set; }
}

But how can I get the dataset to inherit the IEmployeeService interface, and how can I get the TdsCompany.EmployeeRow to implement IEmployee. Notice how the names in the IEmployee interface are the same as the names in the TdsCompany.EmployeeRow class. To add these interfaces I can simply do something like this in the "TdsCompany.cs" (not the autogenerated TdsCompany.designer.cs) file:
partial class TdsCompany : IEmployeeService
{
public partial class EmployeeRow : IEmployee
{
}
public IEmployee GetEmployee(string ssn)
{
EmployeeTableAdapter adapter = new EmployeeTableAdapter();
return adapter.GetEmployeeBySsn(ssn)[0];
}
}

By adding this to the TdsCompany.cs file, my changes won't be deleted if I change the dataset (and I don't want to manually change the .designer.cs anyways). I am also able to limit the visibility of the properties in the EmployeeRow class, and I can even hide some properties or create transformations in the partial class.

Because of these interfaces I am now able to mock out my dataset and web service, and use the excellent Rhino.Mocks to test my task:
_empService = _mocks.CreateMock();
_adrService = _mocks.CreateMock();
_employee = _mocks.CreateMock();
_task = new UpdateAddressTask(_empService, _adrService);

An added benefit is that the class I am using elsewhere in my code is no longer called TdsCompany.EmployeeRow, but has a much nicer name: IEmployee. This sounds much more like a domain object than the database-like EmployeeRow. I am also able to completely remove the dataset at some point in time, without having to change everything around it. The dependencies are gone.
VidarK

Excellent :)

Good stuff.
Mohammad Shalabi

use design patterns

this is an object construction problem. It has no relation with testing. To get red of this problem you can use one of the creation class patterns.
even though the solution will work but it is not generic solution.
Erlend

Re: use design patterns

Thank you for your comment. I don\'t agree that this has no relation to testing. In dataset-driven architectures the objects are often created for you (loaded from database or similar), but when you want to unit test parts of your architecture, this is no longer so. Yes, you can still allow the data adapters to load your data from the database, but then your unit tests will be slow. Using the approach described above, you loosen the couplings by removing the explicit dependencies on the dataset. This is basically \"The Dependency Inversion Principle\" (DIP) put to use. In my humble opinion that principle is pretty generic. On the other hand this was just a proposed solution for dataset-driven architectures. It was not presented as a generic solution.
Comments closed for this post