Testing your logic

Well, this article will be quite short, because when you isolate yourself from sharepoint, or any other hard to test framework, you can write test the same way you would do in any other environment. Now it doesn’t matter if you are a sharepoint developer or a mvc developer, you have plain old business object everywhere and any boundary can be replaced with a test double.

Let’s see a concrete example of what I’m saying. Picking a recent example from a project I’m working on let’s implement the following requirement:

Given that a user can be in multiple groups, and that a company is related to 2 groups, find all the companies that are related to the current user

To do this we need two data sources:

  • One provides the list of groups the current user is in
  • The other provides the list of all the companies and its related groups

This data will be provided by two different repositories with the following interfaces:

public class Group
{
public string Name { get; set; }
}
public interface IGroupRepository
{
IEnumerable<Group> GetCurrentUserGroups();
}
public class Company
{
public string Name { get; set; }
public Group Reader { get; set; }
public Group Editor { get; set; }
}
public interface ICompanyRepository
{
IEnumerable<Company> All();
}

OK now we can start TDDing our logic. We start with the most basic test and work our way up till we are done.

public class Tests
{
[Test]
public void NoCompanyNoGroups_NoCompany
{
var sut = new UserManager(new NoCompanyRepository(), new NoGroupRepository());
var companies = sut.GetRelatedCompanies();
Assert.That(companies.Count(), Is.EqualTo(0));
}
}

This obviously fails, it doesn’t even compile. Let’s pretend we have the No___Repository stubs (if you have trouble implementing them ask in the comments). Let’s write the simplest manager that can pass that test

public class UserManager
{
private readonly ICompanyRepository companyRepo;
private readonly IGroupRepository groupRepo;
public UserManager(ICompanyRepository companyRepo, IGroupRepository groupRepo)
{
this.companyRepo = companyRepo;
this.groupRepo = groupRepo;
}
public IEnumerable<Company> GetRelatedCompanies()
{
return new List<Company>();
}
}

Great this passes, but obviously isn’t what we were after. Let’s write another test

[Test]
public void RightCompanyAndGroup_OneCompany()
{
var sut = new UserManager(new OneCompanyRepository(), new OneEditorGroupRepository());
var companies = sut.GetRelatedCompanies();
Assert.That(companies.Count(), Is.EqualTo(1));
Assert.That(companies.First().Name, Is.EqualTo("Right"));
}
public class OneCompanyRepository : ICompanyRepository
{
public IEnumerable<Company> All()
{
return new List<Company>()
{
new Company
{
Editor = new Group {Name = "Editor"},
Reader = new Group {Name = "Reader"},
Name = "Right"
}
};
}
}
public class OneEditorGroupRepository : IGroupRepository
{
public IEnumerable<Group> GetCurrentUserGroups()
{
return new List<Group>() { new Group { Name = "Editor" } };
}
}

Given the new test the simplest manager can return all the companies from the repository without filter, so we need to test a negative result. The following test checks that we don’t return any company given a set of unrelated groups.

[Test]
public void CompanyAndWrongGroup_NoCompany()
{
var sut = new UserManager(new OneCompanyRepository(), new OneUnreleatedGroupRepository());
var companies = sut.GetRelatedCompanies();
Assert.That(companies.Count(), Is.EqualTo(0));
}
public class OneUnreleatedGroupRepository : IGroupRepository
{
public IEnumerable<Group> GetCurrentUserGroups()
{
return new List<Group>() { new Group { Name = "Unreleated" } };
}
}

Now our implementation can finally be compleated

[Test]
public void CompanyAndWrongGroup_NoCompany()
{
var sut = new UserManager(new OneCompanyRepository(), new OneUnreleatedGroupRepository());
var companies = sut.GetRelatedCompanies();
Assert.That(companies.Count(), Is.EqualTo(0));
}
public class OneUnreleatedGroupRepository : IGroupRepository
{
public IEnumerable<Group> GetCurrentUserGroups()
{
return new List<Group>() { new Group { Name = "Unreleated" } };
}
}

As you can see we have implemented a (simple) bit of business logic that in production will be dealing with objects coming from sharepoint, but that thanks to the layer of repositories, we were able to isolate and build using TDD

Author: Maurizio Pozzobon

Maurizio has 5+ years developing solutions in the insurance industry. He is passionate about doing the right thing right, so he works in a tight loop with his clients to deliver the best solution possible.