Test Driven Development – Sample

In this post, I’m going to create a small program and show how to perform Test Driven Development (TDD) to make your life easier as a programmer.  Instead of creating the typical throw-away program, I’m going to do something a little more complicated.  I’m going to create a .Net Core Web API application that calls another API to get an address from a database.  The “Other” API will not be presented in this sample post because we don’t need the other API in order to write our code.  I’m going to show how to setup unit tests to pretend like there are results coming back from another API and then write the code based on the unit tests.  In real life, this scenario can happen when parallel development efforts occur.  Sometimes a fake API must be developed to finish the work being performed to access the API.  In this case, I’m going to skip the fake API part and just mock the call to the API and feed sample data back.

To get started, all we need is an empty business logic project and a unit test project.  We don’t need to wire-up any of the API front end stuff because we’re going to exercise the business logic from unit tests.  Here’s the scenario:

  1. Our API will accept an address in JSON format, plus a person id from a database.
  2. The result will be true if the database contains the same address information submitted to the API for the person id given.

Ultimately, we’ll have a business object that is instantiated by the IOC container.  Let’s focus on the business object and see if we can come up with an empty shell.  For now we’ll assume that we need an address to compare with and the person id.

public class CheckAddress
{
  public bool IsEqual(int personId, AddressPoco currentAddress)
  {
    
  }
}

There will be a POCO for the address information.  We’ll use the same POCO for the data returned as for the method above.:

public class AddressPoco
{
  public string Address { get; set; }
  public string City { get; set; }
  public string State { get; set; }
  public string Zip { get; set; }
}

So far, so good.  Inside the IsEqual() method in the CheckAddress class above, we’re going to call our address lookup API and then compare the result with the “currentAddress” value.  If they are equal, then we’ll return true.  Otherwise false.  To call another API, we could write an object like this:

public class AddressApiLookup
{
  private const string Url = "http://myurl.com";

  public AddressPoco Get(int personId)
  {
    using (var webClient = new WebClient())
    {
      webClient.Headers["Accept-Encoding"] = "UTF-8";
      webClient.Headers["Content-Type"] = "application/json";

      var arr = webClient.DownloadData(new Uri($"{Url}/api/GetAddress/{personId}"));
      return JsonConvert.DeserializeObject<AddressPoco>(Encoding.ASCII.GetString(arr));
    }
  }
}

In our IOC container, we’ll need to make sure that we break dependencies with the AddressApiLookup, which means we’ll need an interface.  We’ll also need an interface for our CheckAddress object, but that interface will not be needed for this round of unit tests.  Here’s the interface for the AddressApiLookup object:

public interface IAddressApiLookup
{
  AddressPoco Get(int personId);
}

Now we can mock the AddressApiLookup by using Moq.  Don’t forget to add the interface to the class declaration, like this:

public class AddressApiLookup : IAddressApiLookup

One last change you’ll need to perform: The CheckAddress object will need to have the AddressApiLookup injected in the constructor.  Your IOC container is going to perform the injection for you when your code is complete, but for now, we’re going to inject our mocked object into the constructor.  Change your object to look like this:

public class CheckAddress
{
  private IAddressApiLookup _addressApiLookup;

  public CheckAddress(IAddressApiLookup addressApiLookup)
  {
    _addressApiLookup = addressApiLookup;
  }

  public bool IsEqual(int personId, AddressPoco currentAddress)
  {
    
  }
}

You can setup the usual unit tests, like this:

  1. Both addresses are alike
  2. Addresses are different
  3. No address returned form remote API

You’ll probably want to test other scenarios like a 500 error, but you’ll need to change the behavior of the API calling method to make sure you return the result code.  We’ll stick to the simple unit tests for this post.  Here is the first unit test:

[Fact]
public void EqualAddresses()
{
  // arrange
  var address = new AddressPoco
  {
    Address = "123 main st",
    City = "Baltimore",
    State = "MD",
    Zip = "12345"
  };

  var addressApiLookup = new Mock<IAddressApiLookup>();
  addressApiLookup.Setup(x => x.Get(1)).Returns(address);

  // act
  var checkAddress = new CheckAddress(addressApiLookup.Object);
  var result = checkAddress.IsEqual(1, address);

  //assert
  Assert.True(result);
}

In the arrange segment, the address POCO is populated with some dummy data.  This data is used by both the API call (the mock call) and the CheckAddress object.  That guarantees that we get an equal result.  We’ll use “1” as the person id, that means that we’ll need to use “1” in the mock setup and “1” in the call to the IsEqual method.  Otherwise, we can code the setup to use “It.IsAny<int>()” as a matching input parameter and any number in the IsEqual method call.

The act section creates an instance of the CheckAddress object and injects the mocked AddressApiLookup object.  Then the result is obtained from a call to the IsEqual with the same address passed as a parameter.  The assert just checks to make sure it’s all true.

If you execute your unit tests here, you’ll get a failure.  For now, let’s go ahead and write the other two unit tests:

[Fact]
public void DifferentAddresses()
{
  // arrange
  var addressFromApi = new AddressPoco
  {
    Address = "555 Bridge St",
    City = "Washington",
    State = "DC",
    Zip = "22334"
  };

  var address = new AddressPoco
  {
    Address = "123 main st",
    City = "Baltimore",
    State = "MD",
    Zip = "12345"
  };

  var addressApiLookup = new Mock<IAddressApiLookup>();
  addressApiLookup.Setup(x => x.Get(1)).Returns(addressFromApi);

  // act
  var checkAddress = new CheckAddress(addressApiLookup.Object);
  var result = checkAddress.IsEqual(1, address);

  //assert
  Assert.False(result);
}

[Fact]
public void NoAddressFound()
{
  // arrange
  var addressFromApi = new AddressPoco
  {
  };

  var address = new AddressPoco
  {
    Address = "123 main st",
    City = "Baltimore",
    State = "MD",
    Zip = "12345"
  };

  var addressApiLookup = new Mock<IAddressApiLookup>();
  addressApiLookup.Setup(x => x.Get(1)).Returns(addressFromApi);

  // act
  var checkAddress = new CheckAddress(addressApiLookup.Object);
  var result = checkAddress.IsEqual(1, address);

  //assert
  Assert.False(result);
}

In the DifferentAddress test, I had to setup two addresses, one to be returned by the mocked object and one to be fed into the IsEqual method call.  For the final unit test, I created an empty POCO for the address used by the API.

Now the only task to perform is to create the code to make all the tests pass.  To perform TDD to the letter, you would create the first unit test and then insert code to make that unit test pass.  For that case, you can just return a true and the first unit test will pass.  Then create the second unit test and refactor the code to make that unit test pass.  Writing two or more unit tests before you start creating code can sometimes save you the time of creating a trivial code solution.  That’s what I’ve done here.  So let’s take a stab at writing the code:

public bool IsEqual(int personId, AddressPoco currentAddress)
{
  return currentAddress == _addressApiLookup.Get(personId);
}

Next, run your unit tests and they will all pass.

Except, there is one possible problem with the tests that were created: The equality might be checking only the memory address to the address POCO instance.  In that case, the equal unit test might not be testing if the data inside the object is the same.  So let’s change the equal address unit test to use two different instances of address with the same address (copy one of them and change the name):

[Fact]
public void EqualAddresses()
{
  // arrange
  var addressFromApi = new AddressPoco
  {
    Address = "123 main st",
    City = "Baltimore",
    State = "MD",
    Zip = "12345"
  };
  var address = new AddressPoco
  {
    Address = "123 main st",
    City = "Baltimore",
    State = "MD",
    Zip = "12345"
  };

  var addressApiLookup = new Mock<IAddressApiLookup>();
  addressApiLookup.Setup(x => x.Get(1)).Returns(addressFromApi);

  // act
  var checkAddress = new CheckAddress(addressApiLookup.Object);
  var result = checkAddress.IsEqual(1, address);

  //assert
  Assert.True(result);
}

Now, if you run the unit tests you’ll get the following result:

Aha!  Just as I suspected.  This means that we need to refactor our method to properly compare the two POCO objects.  You’ll have to implement IComparable inside the AddressPoco:

public class AddressPoco : IComparable
{
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }

    public int CompareTo(AddressPoco other)
    {
        if (ReferenceEquals(this, other)) return 0;
        if (ReferenceEquals(null, other)) return 1;
        var addressComparison = string.Compare(Address, other.Address, StringComparison.Ordinal);
        if (addressComparison != 0) return addressComparison;
        var cityComparison = string.Compare(City, other.City, StringComparison.Ordinal);
        if (cityComparison != 0) return cityComparison;
        var stateComparison = string.Compare(State, other.State, StringComparison.Ordinal);
        if (stateComparison != 0) return stateComparison;
        return string.Compare(Zip, other.Zip, StringComparison.Ordinal);
    }
}

I have ReSharper installed on my machine, so it has an auto-generate CompareTo method.  That auto-generate created the method and the code inside it.  You can also override the equals and use the equal sign, but this is simpler.  Next, you’ll have to modify the IsEqual() method to use the CompareTo() method:

public bool IsEqual(int personId, AddressPoco currentAddress)
{
  return currentAddress.CompareTo(_addressApiLookup.Get(personId)) == 0;
}

Now run your unit tests:

Where to Get the Sample Code

You can go to my GitHub account to download the sample code used in this blog post by going here.  I swear by ReSharper and I have purchased the Ultimate version (so I can use the unit test coverage tool).  This product for an individual is approximately $150 (first time) or less for the upgrade price.  ReSharper is one of the best software products I’ve ever bought.

 

Unit Testing EF Data With Moq

Introduction

I’ve discussed using the in-memory Entity Framework unit tests in a previous post (here).  In this post, I’m going to demonstrate a simple way to use Moq to unit test a method that uses Entity Framework Core.

Setup

For this sample, I used the POCOs, context and config files from this project (here).  You can copy the cs files from that project, or you can just download the sample project from GitHub at the end of this article.

You’ll need several parts to make your unit tests work:

  1. IOC container – Not in this post
  2. List object to DbSet Moq method
  3. Test data
  4. Context Interface

I found a method on stack overflow (here) that I use everywhere.  I created a unit test helper static object and placed it in my unit test project:

public class UnitTestHelpers
{
  public static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
  {
    var queryable = sourceList.AsQueryable();

    var dbSet = new Mock<DbSet<T>>();
    dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
    dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
    dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
    dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
    dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>(sourceList.Add);

    return dbSet.Object;
  }
}

The next piece is the pretend data that you will use to test your method.  You’ll want to keep this as simple as possible.  In my implementation, I allow for multiple data sets.

public static class ProductTestData
{
  public static List Get(int dataSetNumber)
  {
    switch (dataSetNumber)
    {
      case 1:
      return new List
      {
        new Product
        {
          Id=0,
          Store = 1,
          Name = "Cheese",
          Price = 2.5m
        },
        ...

      };
    }
    return null;
  }
}

Now you can setup a unit test and use Moq to create a mock up of your data and then call your method under test.  First, let’s take a look at the method and see what we want to test:

public class ProductList
{
  private readonly IDatabaseContext _databaseContext;

  public ProductList(IDatabaseContext databaseContext)
  {
    _databaseContext = databaseContext;
  }

  public List GetTopTen()
  {
    var result = (from p in _databaseContext.Products select p).Take(10).ToList();

    return result;
  }
}

The ProductList class will be setup from an IOC container.  It has a dependency on the databaseContext object.  That object will be injected by the IOC container using the class constructor.  In my sample code, I set up the class for this standard pattern.  For unit testing purposes, we don’t need the IOC container, we’ll just inject our mocked up context into the class when we create an instance of the object.

Let’s mock the context:

[Fact]
public void TopTenProductList()
{
  var demoDataContext = new Mock<IDatabaseContext>();

}

As you can see, Moq uses interfaces to create a mocked object.  This is the only line of code we need for the context mocking.  Next, we’ll mock some data.  We’re going to tell Moq to return data set 1 if the Products getter is called:

[Fact]
public void TopTenProductList()
{
  var demoDataContext = new Mock<IDatabaseContext>();
  demoDataContext.Setup(x => x.Products).Returns(UnitTestHelpers.GetQueryableMockDbSet(ProductTestData.Get(1)));

}

I’m using the GetQueryableMockDbSet unit test helper method in order to convert my list objects into the required DbSet object.  Any time my method tries to read Products from the context, data set 1 will be returned.  This data set contains 12 items.  As you can see from the method that we are going to mock up, there should be only ten items returned.  Let’s add the method under test setup:

[Fact]
public void TopTenProductList()
{
  var demoDataContext = new Mock<IDatabaseContext>();
  demoDataContext.Setup(x => x.Products).Returns(UnitTestHelpers.GetQueryableMockDbSet(ProductTestData.Get(1)));

  var productList = new ProductList(demoDataContext.Object);

  var result = productList.GetTopTen();
  Assert.Equal(10,result.Count);
}

The object under test is very basic, just get an instance and pass the mocked context (you have to use .Object to get the mocked object).  Next, just call the method to test.  Finally, perform an assert to conclude your unit test.  If the productList() method returns an amount that is not ten, then there is an issue (for this data set).  Now, we should test an empty set.  Add this to the test data switch statement:

case 2:
  return new List
  {
  };

Now the unit test:

[Fact]
public void TopTenProductList()
{
  var demoDataContext = new Mock<IDatabaseContext>();
  demoDataContext.Setup(x => x.Products).Returns(UnitTestHelpers.GetQueryableMockDbSet(ProductTestData.Get(2)));

  var productList = new ProductList(demoDataContext.Object);

  var result = productList.GetTopTen();
  Assert.Empty(result.Count);
}

All the work has been done to setup the static test data object, so I only had to add one case to it.  Then the unit test is identical to the previous unit test, except the ProductTestData.Get() has a parameter of 2, instead of 1 representing the data set number.  Finally, I changed the assert to test for an empty set instead of 10.  Execute the tests:

Now you can continue to add unit tests to test for different scenarios.

Where to Get the Code

You can go to my GitHub account and download the sample code (click here).  If you would like to create the sample tables to make this program work (you’ll need to add your own console app to call the GetTopTen() method), you can use the following MS SQL Server script:

CREATE TABLE [dbo].[Store](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [varchar](50) NULL,
	[Address] [varchar](50) NULL,
	[State] [varchar](50) NULL,
	[Zip] [varchar](50) NULL,
 CONSTRAINT [PK_Store] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[Product](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Store] [int] NOT NULL,
	[Name] [varchar](50) NULL,
	[Price] [money] NULL,
 CONSTRAINT [PK_Product] PRIMARY KEY NONCLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[Product]  WITH CHECK ADD  CONSTRAINT [FK_store_product] FOREIGN KEY([Store])
REFERENCES [dbo].[Store] ([Id])
GO

ALTER TABLE [dbo].[Product] CHECK CONSTRAINT [FK_store_product]
GO
 

Unit Tests are not an Option!

Introduction

I’ve been writing software since 1978.  Which is to say that I’ve seen many paradigm changes.  I witnessed the inception of object oriented programming.  I first became aware of objects when I read a Byte magazine article on a language called SmallTalk (issued August 1981).  I read and re-read that article many times to try and understand what the purpose of object oriented programming was.  Object oriented programming took ten more years before programmers began to recognize it.  In the early 90’s the University of Michigan only taught a few classes using object oriented C++.  It was still new and shiny.  Now all languages are object oriented, or they are legacy languages.

The web was another major paradigm that I witnessed.  Before the browser was invented (while I was in college), all programs were written to be executed on the machine it was run on.  I was immersed in the technology of the Internet while I was a student at UofM and we used tools such as Telnet, FTP, Archie, DNS, and Gopher (to name a few), to navigate and find information.  The Internet was primarily composed of data about programming.  When Mosaic came along as well as HTML, the programming world went crazy.  The technology didn’t mature until the early 2000’s.  Many programming languages were thrown together to accommodate the new infrastructure (I’m looking at you “Classic ASP”).

Extreme programming came of age in the late 90’s.  I did not get involved in XP until the mid 2000’s.  Waterfall was the way things were done.  The industry was struggling with automated testing suites.  Unit testing came onto the scene, but breaking dependencies was an unknown quantity.  It took some years before somebody came up with the idea of inversion of control.  The idea was so abstract that most programmers ignored it and moved on.

The latest paradigm change, and it’s much bigger than most will give it credit for is the IOC container.  Even Microsoft has incorporated this technology into their latest tools.  IOC is part of .Net Core.  If you’re a programmer and you haven’t used IOC containers yet, or you don’t understand the underlying reason for it, you better get on the bandwagon.  I predict that within five years, IOC will be recognized as the industry standard, even for companies that build software for their internal use only.  It will be difficult to get a job as a programmer without understanding this technology.  Don’t believe me?  Pretend you’re a software engineer with no object oriented knowledge.  Now search for a job and see what results come up.  Grim, isn’t it?

Where am I going with this?  I currently work for a company that builds medical software.  We have a huge collection of legacy code.  I’m too embarrassed to admit how large this beast is.  It just makes me cry.  Our company uses the latest tools and we have advanced developers who know how to build IOC containers, unit tests, properly scoped objects, etc.  We also practice XP, to a limited degree.  We do the SCRUMs, stand-ups, code-reviews (sometimes), demos, and sprint planning meetings.  What we don’t do is unit testing.  Oh we have unit tests, but the company mandate is that they are optional.  When there is extra time to build software, unit tests are incorporated.  Only a small hand-full of developers incorporate unit tests into their software development process.  Even I have built some new software without unit tests (and I’ve paid the price).

The bottom line is that unit tests are not part of the software construction process.  The company is staffed with programmers that are unfamiliar with TDD (Test Driven Development) and in fact, most are unfamiliar with unit testing altogether.  Every developer has heard of unit test, but I suspect that many are not sold on the concept of the unit test.  Many developers look at unit testing as just more work.  There are the usual arguments against unit testing: They need to be maintained, they become obsolete, they break when I refactor code, etc.  These are old arguments that were disproved years ago, but, like myths, they get perpetuated forever.

I’m going to divert my the subject a bit here, just to show how crazy this is.

Our senior developers have gathered in many meetings to discuss the agreed upon architecture that we are aiming for.  That architecture is not much different from any other company: Break our monolithic application into smaller APIs, use IOC containers, separate database concerns from business and business from the front-end.  We have a couple dozen APIs and they were written with this architecture in mind.  They are all written with IOC containers.  We use Autofac for our .Net applications and .Net Core has it’s own IOC container technology.  Some of these APIs have unit tests.  These tests were primarily added after the code was written, which is OK.  Some of our APIs have no unit tests.  This is not OK.

So the big question is: Why go through the effort of using an IOC container in the first place, if there is no plan for unit tests?

The answer is usually “to break dependencies.”  Which is correct, except, why?  Why did anybody care about breaking dependencies?  Just breaking dependencies gains nothing.  The IOC container itself, does not help with the maintainability of the code.  Is it safer to refactor code with an IOC container?  No.  Is it easier to troubleshoot and fix bugs in code that has dependencies broken?  Not unless your using unit tests.

My only conclusion to this crazy behavior is that developers don’t understand the actual purpose of unit testing.

Unit Test are Part of the Development Process

The most difficult part of creating unit tests is breaking dependencies.  IOC containers make that a snap.  Every object (with some exceptions) should be put into the container.  If an object instance must be created by another object, then it must be created inside the IOC container.  This will break the dependency for you.  Now unit testing is easy.  Just focus on one object at a time and write tests for that object.  If the object needs other objects to be injected, then us a mocking framework to make mock those objects.

As a programmer, you’ll need to go farther than this.  If you want to build code that can be maintained, you’ll need to build your unit tests first or at least concurrently.  You cannot run through like the Tasmanian devil, building your code, and then follow-up with a hand-full of unit tests.  You might think you’re clever by using a coverage tool to make sure you have full code coverage, but I’m going to show an example where code-coverage is not the only reason for unit testing.  Your workflow must change.  At first, it will slow you down, like learning a new language.  Keep working at it and eventually, you don’t have to think about the process.  You just know.

I can tell you from experience, that I don’t even think about how I’m going to build a unit test.  I just look at what I’ll need to test and I know what I need to do.  It took me years to get to this point, but I can say, hands down, that unit testing makes my workflow faster.  Why?  Because I don’t have to run the program in order to test for all the edge cases.  I write one unit test at a time and I run that test against my object.  I use unit testing as a harness for my objects.  That is the whole point of using an IOC container.  First, you take care of the dependencies, then you focus on one object at a time.

Example

I’m sure you’re riveted by my rambling prose, but I’m going to prove what I’m talking about.  At least on a small scale.  Maybe this will change your mind, maybe it won’t.

Let’s say for example, I was writing some sort of API that needed to return a set of patient records from the database.  One of the requirements is that the calling program can feed filter parameters to select a date range for the records desired.  There is a start date and an end date filter parameter.  Furthermore, each date parameter can be null.  If both are null, then give me all records.  If the start parameter is null, then give me up to the end date.  If the end date is null, then give me from the start date to the latest record.  The data in the database will return a date when the patient saw the doctor.  This is hypothetical, but based on a real program that my company uses.  I’m sure this scenario is used by any company that queries a database for web use, so I’m going to use it.

Let’s say the program is progressing like this:

public class PatienData
{
  private DataContext _context;

  public List<PatientVisit> GetData(int patientId, DateTime ? startDate, DateTime ? endDate)
  {
    var filterResults = _context.PatientVisits.Where(x => x.BetweenDates(startDate,endDate));

    return filterResults.ToList();
  }
}

You don’t want to include the date range logic in your LINQ query, so you create an extension method to handle that part.  Your next task is to write the ugly code called “BetweenDates()”.  This will be a static extension class that will be used with any of your PatientVisit POCOs.  If you’re unfamiliar with a POCO (Plain Old C# Code) object, then here’s a simple example:

public class PatientVisit
{
  public int PatientId { get; set; }
  public DateTime VisitDate { get; set; }
}

This is used by Entity Framework in a context.  If you’re still confused, please search through my blog for Entity Framework subjects and get acquainted with the technique.

Back to the “BetweenDates()” method.  Here’s the empty shell of what needs to be written:

public static class PatientVisitHelpers
{
  public static bool BetweenDates(this PatientVisit patientVisit, DateTime ? startDate, DateTime ? endDate)
  {
    
  }
}

Before you start to put logic into this method, start thinking about all the edge cases that you will be required to test.  If you run in like tribe of Comanche Indians and throw the logic into this method, you’ll be left with a manual testing job that will probably take you half a day (assuming you’re thorough).  Later, down the road, if someone discovers a bug, you will need to fix this method and then redo all the manual tests.

Here’s where unit test are going to make your job easy.  The unit tests are going to be part of the discovery process.  What Discovery?  One aspect of writing software that is different from any other engineering subject is that every project is new.  We don’t know what has to be built until we start to build it.  Then we “discover” aspects of the problem that we never anticipated before.  In this sample, I’ll show how that occurs.

Let’s list the rules:

  1. If the dates are both null, give me all records.
  2. If the first date is null, give me all records up to that date (including the date).
  3. If the last date is null, give me all records from the starting date (including the start date).
  4. If both dates exist, then give me all records, including the start and end dates.

According to this list, there should be at least four unit tests.  If you discover any edge cases, you’ll need a unit tests for each edge case.  If a bug is discovered, you’ll need to add a unit test to simulate the bug and then fix the bug.  Which tells you that you’ll keep adding unit tests to a project every time you fix a bug or add a feature (with the exception that one or more unit tests were incorrect in the first place).  An incorrect unit test usually occurs when you misinterpret the requirements.  In such an instance, you’ll fix the unit test and then fix your code.

Now that we have determined that we need five unit tests, create five empty unit test methods:

public class PatientVisitBetweenDates
{
  [Fact]
  public void BothDatesAreNull()
  {

  }
  [Fact]
  public void StartDateIsNull()
  {

  }
  [Fact]
  public void EndDateIsNull()
  {

  }
  [Fact]
  public void BothDatesPresent()
  {

  }
}

I have left out the IOC container code from my examples.  I am testing a static object that has no dependencies, therefore, it does not need to go into a container.  Once you have established an IOC container and you have broken dependencies on all objects, you can focus on your code just like the samples I am showing here.

Now for the next step: Write the unit tests.  You already have the method stubbed out.  So you can complete your unit tests first and then write the code to make the tests pass.  You can do one unit test, followed by writing code, then the next test, etc.  Another method is to write all the unit tests and then write the code to pass all tests.  I’m going to write all the unit tests first.  By now, you might have analyzed my empty unit tests and realized what I meant earlier by “discovery”.  If you haven’t, then this will be a good lesson.

For the first test, we’ll need the setup data.  We don’t have to concern ourselves with any of the Entity Framework code other than the POCO itself.  In fact, the “BetweenDates()” method only looks at one instance, or rather, one record.  If the date of the record will be returned with the set, then the method will return true.  Otherwise, it should return false.  The tiny scope of this method makes our unit testing easy.  So put one record of data in:

[Fact]
public void BothDatesAreNull()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };
}

Next, setup the object and perform an assert.  This unit test should return a true for the data given because both the start date and the end date passed into our method will be null and we return all records.

[Fact]
public void BothDatesAreNull()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };

  var result = testSample.BetweenDates(null,null);
  Assert.True(result);
}

This test doesn’t reveal anything yet.  Technically, you can put code into your method that just returns true, and this test will pass.  At this point, it would be valid to do so.  Then you can write your next test and then refactor to return the correct value.  This would be the method used for pure Test Driven Development.  Only use the simplest code to make the test pass.  The code will be completed when all unit tests are completed and they all pass.

I’m going to go on to the next unit test, since I know that the first unit test is a trivial case.  Let’s use the same data we used on the last unit test:

[Fact]
public void StartDateIsNull()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };
}

Did you “discover” anything yet?  If not, then go ahead and put the method setup in:

[Fact]
public void StartDateIsNull()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };
  
  var result = testSample.BetweenDates(null, DateTime.Parse("1/8/2015"));
}

Now, you’re probably scratching your head because we need at least two test cases and probably three.  Here are the tests cases we need when the start date is null but the end date is filled in:

  1. Return true if the visit date is before the end date.
  2. Return false if the visit date is after the end date.

What if the date is equal to the end date?  Maybe we should test for that edge case as well.  Break the “StartDateIsNull()” unit test into three unit tests:

[Fact]
public void StartDateIsNullVisitDateIsBefore()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };
  var result = testSample.BetweenDates(null, DateTime.Parse("1/3/2015"));
  Assert.True(result);
}
[Fact]
public void StartDateIsNullVisitDateIsAfter()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };
  var result = testSample.BetweenDates(null, DateTime.Parse("1/8/2015"));
  Assert.False(result);
}
[Fact]
public void StartDateIsNullVisitDateIsEqual()
{
  var testSample = new PatientVisit
  {
    PatientId = 1,
    VisitDate = DateTime.Parse("1/7/2015")
  };
  var result = testSample.BetweenDates(null, DateTime.Parse("1/7/2015"));
  Assert.True(result);
}

Now you can begin to see the power of unit testing.  Would you have manually tested all three cases?  Maybe.

That also reveals that we will be required to expand the other two tests that contain dates.  The test case where we have a null end date will have a similar set of three unit tests and the in-between dates test will have more tests.  For the in-between, we now need:

  1. Visit date is less than start date.
  2. Visit date is greater than start date but less than end date.
  3. Visit date is greater than end date.
  4. Visit date is equal to start date.
  5. Visit date is equal to end date.
  6. Visit date is equal to both start and end date (start and end are equal).

That makes 6 unit test for the in-between case.  Bringing our total to 13 tests.

Fill in the code for the remaining tests.  When that is completed, verify each test to make sure they are all valid cases.  Once this is complete, you can write your code for the helper method.  You now have a complete detailed specification for your method written in unit tests.

Was that difficult?  Not really.  Most unit tests fall into this category.  Sometimes you’ll need to mock an object that your object under test depends on.  That is made easy by the IOC container.

Also, you can execute your code directly from the unit test.  Instead of writing a test program to send inputs to your API, or using your API in a full system where you are typing data in manually, you just execute the unit test you are working with.  You type in your code, then run all the unit tests for this method.  As you create code to account for each test case, you’ll see your unit tests start to turn green.  When all unit tests are green, you’re work is done.

Now, if Quality finds a bug that leads to this method, you can reverify your unit tests for the case that QA found.  You might discover a bug in code that is outside your method or it could have been a case missed by your unit tests.  Once you have fixed the bug, you can re-run the unit tests instead of manually testing each case.  In the long run, this will save you time.

Code Coverage

You should strive for 100% code coverage.  You’ll never get it, but the more code you can cover, the safer it will be to refactor code in the future.  Any code not covered by unit tests is at risk for failure when code is refactored.  As I mentioned earlier, code coverage doesn’t solve all your problems.  In fact, if I wrote the helper code for the previous example and then I created unit tests afterwards, I bet I can create two or three unit tests that covers 100% of the code in the helper method.  What I might not cover are edge cases, like the visit date equal to the start date.  It’s best to use code coverage tools after the code and unit tests are written.  The code coverge will be your verfication that you didn’t miss something.

Another problem with code coverage tools is that it can make you lazy.  You can easily look at the code and then come up with a unit test that executes the code inside an “if” statement and then create a unit test to execute code inside the “else” part.  The unit tests might not be valid.  You need to understand the purpose of the “if” and “else” and the purpose of the code itself.  Keep that in mind.  If you are writing new code, create the unit test first or concurrently.  Only use the code coverage tool after all your tests pass to verify you covered all of your code.

Back to the 20,000 foot View

Let’s take a step back and talk about what the purpose of the exercise was.  If you’re a hold-out for a world of programming without unit tests, then you’re still skeptical of what was gained by performing all that extra work.  There is extra code.  It took time to write that code.  Now there are thirteen extra methods that must be maintained going forward.

Let’s pretend this code was written five years ago and it’s been humming along for quite some time without any bugs being detected.  Now some entry-level developer comes on the scene and he/she decides to modify this code.  Maybe the developer in question thinks that tweaking this method is an easy short-cut to creating some enhancement that was demanded by the customer.  If the code is changed and none of the unit tests break, then we’re OK.  If the code is changed and one or more unit tests breaks, then the programmer modifying the code must look at those unit tests and determine if the individual behavoirs should be changed, or maybe those were broken because the change is not correct.  If the unit tests don’t exist, the programmer modifying the code has no idea what thought process and/or testing went into the original design.  The programmer probably doesn’t know the full specification of the code when it was written.  The suite of unit tests make the purpose unambiguous.  Any programmer can look at the unit tests and see exactly what the specification is for the method under test.

What if a bug is found and all unit tests pass?  What you have discovered is an edge case that was not known at the time the method was written.  Before fixing the bug, the programmer must create a unit test with the edge case that causes the bug.  That unit test must fail with the current code and it should fail in the same manner as the real bug.  Once the failing unit test is created, then the bug should be fixed to make the unit test pass.  Once that has been accomplished, run all unit tests and make sure you didn’t break prevous features when fixing the bug.  This method ends the whack-a-mole technique of trying to fix bugs in software.

Next, try to visualize a future where all your business code is covered by unit tests.  If each class and method had a level of unit testing to the specification that this mehod has, it would be safe and easy to refactor code.  Any refactor that breaks code down the line will show up in the unit tests (as broken tests).  Adding enhancements would be easy and quick.  You would be virtually guarenteed to produce a quality product after adding an enhancement.  That’s because you are designing the software to be maintainable.

Not all code can be covered by unit tests.  In my view, this is a shame.  Unfortunately, there are sections of code that cannot be put into a unit test for some reason or another.  With an IOC container, your projects should be divided into projects that are unit testable and projects that are not.  Projects, such as the project containiner your Entity Framework repository, are not unit testable.  That’s OK, and you should limit how much actual code exists in this project.  All the code should be POCO’s and some connecting code.  Your web interface should be limited to code that connects the outside world to your business classes.  Any code that is outside the realm of unit testing is going to be difficult to test.  Try to limit the complexity of this code.

Finally…

I have looked over the shoulder of students building software for school projects at the University of Maryland and I noticed that they incorporated unit testing into a Java project.  That made me smile.  While the project did not contain an IOC container, it’s a step in the right direction.  Hopefully, withing the next few years, universities will begin to produce students that understand that unit tests are necessary.  There is still a large gap between those students and those in the industry that have never used unit tests.  That gap must be filled in a self-taught manner.  If you are one of the many who don’t incorporate unit testing into your software development process, then you better start doing it.  Now is the time to learn and get good at it.  If you wait too long, you’ll be one of those Cobol developers that wondered who moved their cheese.