
Looking at the code from the first post, it works fine (at least for a stripped-down demo). However, there are a few problems with it. If you want to test that the program works, you need to manually run the program, enter some data, and see the results. It’s possible to create some scripts to run the program, but they tend to be fragile and can start breaking if you modify the UI (like changing the “State Code” textbox to a drop-down list). You also need to always be able to contact the third-party web service to do your testing. If the service is down, your test can fail, even though your code is functioning as you expect. You are also dependent on the results returned from the web service. If you are connecting to a test site, they may clean out their database, causing your previously valid input to fail.
A common opinion of unit tests is that they should not require any external resource. I like to think of my program loaded in memory. Anything outside of that memory is an external resource that could potentially fail due to something that isn’t related to the program. If I need to write to disk, the disk may be full or the network may be down. Sending out an e-mail may fail because the mail server has not been set to allow outbound mail from my computer. The Internet may be down, and nobody has a backup copy (well…you know what I mean). You may also have a problem if everything is working. How are you going to test how your program handles the database server being down, other than stopping the database server (which you may not have rights to do)? With mock objects, you create a simulation of these external resources, and you have complete control over how you want them to react.
By the way, I want to be sure to mention that you still need to test how your program works with the real external resources. However, that’s integration testing. Unit testing is just your first round of testing.
In order to make this program testable, we’re going to do some dependency injection, then create mock objects that let us test all our code paths without needing to worry about some other system being stable. Right now, the PropertyAddress class is dependent on the web service. We’re going to invert it by passing the web service object into its constructor.
First we’ll create a new class in the BusinessObjects project. It will be named AddressValidator. The only method it will have on it is the IsAddressValid method that is being used in the PropertyAddress class. This method will call the web service, to do the data validation. The code looks like this:
AddressValidator.cs
using System;
using BusinessObjects.AddressValidationService;
namespace BusinessObjects
{
public class AddressValidator
{
public bool IsAddressValid(string streetAddress, string city, string stateCode, string zipCode)
{
ValidationService validator = new ValidationService();
return validator.IsAddressValid( streetAddress, city, stateCode, zipCode );
}
}
}
Next, we’ll modify the PropertyAddress class to start removing the dependency on the web service. There are a few different ways to do this, but I’ll do a fairly simple method. We create a constructor that initializes a private variable that holds this AddressValidator object. Then, we have the Save method use this private variable. The new code looks like this:
PropertyAddress.cs
using System;
namespace BusinessObjects
{
public class PropertyAddress
{
private AddressValidator myValidator;
public PropertyAddress()
{
myValidator = new AddressValidator();
}
public void Save(string streetAddress, string city, string stateCode, string zipCode)
{
if(myValidator.IsAddressValid(streetAddress, city, stateCode, zipCode))
{
// Code to save to the database would go here.
}
else
{
throw new ArgumentException( “Property address is not valid. Data was not saved.” );
}
}
}
}
After making these changes, I ran the program and saw that everything is still working. One of my favorite practices of Agile development is making small changes and testing them often. This way, I don’t spend eight hours, writing hundreds of lines of code, then trying to figure out exactly which change caused the program to break. If something stops working, I know it was something I changed in the last few minutes, since all the tests were successful a few minutes ago.
As you probably noticed, the PropertyAddress class is still dependent on the web service. We just moved the instantiation of the web service object to another place (I know I have some smart readers out there). We still need a way to pass in our mock objects. In order to do that, we’ll create another constructor that accepts an AddressValidator object. But, we’ll actually have it accept an interface as the parameter’s datatype. The interface just defines what properties and methods the class needs to have. In the BusinessObjects project, create this IAddressValidator interface:
using System;
namespace BusinessObjects
{
public interface IAddressValidator
{
bool IsAddressValid( string streetAddress, string city, string stateCode, string zipCode );
}
}
Go back to the AddressValidator class and change the line:
public class AddressValidator
to this, in order to say that this class implements the IAddressValidator interface:
public class AddressValidator : IAddressValidator
Then, go back into PropertyAddress.cs and change the line:
private AddressValidator myValidator;
to this, so that the private variable is a type of the interface:
private IAddressValidator myValidator;
While you’re in PropertyAddress.cs, add a new constructor that takes a parameter of IAddressValidator and assigns it to the private variable. Now, PropertyAddress.cs should look like this:
using System;
namespace BusinessObjects
{
public class PropertyAddress
{
private IAddressValidator myValidator;
public PropertyAddress( IAddressValidator validator )
{
myValidator = validator;
}
public PropertyAddress()
{
myValidator = new AddressValidator();
}
public void Save(string streetAddress, string city, string stateCode, string zipCode)
{
if(myValidator.IsAddressValid(streetAddress, city, stateCode, zipCode))
{
// Code to save to the database would go here.
}
else
{
throw new ArgumentException( “Property address is not valid. Data was not saved.” );
}
}
}
}
We’re now at the point where we have the ability to inject our dependent object into our class. In the next post, we’ll write some unit tests and some mock objects to use in those tests.