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.

 

Leave a Reply