Dependency Injection from the Trenches

Dependency Injection from the Trenches

The Problem

More often than not I encounter code like this where I work:

public class MyBusinessLogic{
	public MyBusinessLogic(){}
	public bool IsValid(Contract contract){
		foreach (Participant p in contract.Participants){
			Customer customer = Utils.GetCustomer(p.CustomerID);
			if (String.IsNullOrEmpty(customer.Name))
				return false;
		}
		return true;
	}
}

And Utils is defined like this:

public class Utils{
	public static Customer GetCustomer(int customerID){
		string url = ConfigurationManager.AppSettings["mywebserviceurl"];
		CustomerWebService service = new CustomerWebService(url);
		return service.GetCustomer(customerID);
	}
}

What's the Problem?

MyBusinessLogic is tightly coupled to the real webservice. 

IsValid only works if your connection to the webservice exists. 

How can you test IsValid without be sure of the results the webservice might return? You don't own the webservice, so there's no direct access to the data.  There's no way to guarentee that your test that worked today will work tomorrow, if say the customer no longer fits your criteria anymore. 

The only way to test this out is to waste a bunch of time data mining for use cases that might exercise your code.

Dependency Injection Please

What we have now is a dependency on the external resource, the web service. We need to extract this dependency, and we'll do that with an interface. This is the is what the interface looks like:

public interface ICustomerWebService{
	Customer GetCustomer(int customerID);
}

Next, we're going to change MyBusinessLogic from this:

public class MyBusinessLogic{
	public MyBusinessLogic(){}
	public bool IsValid(Contract contract){
		foreach (Participant p in contract.Participants){
			Customer customer = Utils.GetCustomer(p.CustomerID);
			if (String.IsNullOrEmpty(customer.Name))
				return false;
		}
		return true;
	}
}

...to having one constructor that requires an instance of something that implements ICustomerWebService. Then change the code in IsValid to use the instance passed to the constructor to get the customer like this:

public class MyBusinessLogic{
	private ICustomerWebService _service;
	public MyBusinessLogic(ICustomerWebService service){
		_service = service;
	}
	public bool IsValid(Contract contract){
		foreach (Participant p in contract.Participants){
			Customer customer = _service.GetCustomer(p.CustomerID);
			if (String.IsNullOrEmpty(customer.Name))
				return false;
		}
		return true;
	}
}

How Do You Test It??

One test could be written this way(using Rhino Mocks to mock the service):

[Test]
public void ContractShouldBeValidIfAllCustomerNamesAreNotEmptyAndNotNull(){			
	MockRepository mocks = new MockRepository();
	ICustomerWebService service = mocks.Stub();

	// Create the customer instance we're testing our code against...
	Customer aCustomer = new Customer();
	aCustomer.Name = "Chris Carter";
	
	// Create the expectation that a call to an instance of something that implements
	// ICustomerWebService.GetCustomer returns the customer we just 
	// created(our code doesn't know we made it up)
	int customerID = 123;
	Expect.Call(service.GetCustomer(customerID)).Return(aCustomer);

	// Create the input that the business method expects to receive.
	Contract contract = new Contract();
	contract.Participants.Add(new Participant(customerID));

	mocks.ReplayAll();

	// THIS IS THE DEPENDENCY INJECTION !!!
	MyBusinessLogic logic = new MyBusinessLogic(service);

	// Verify that our business logic works against a service that returned a
	// customer where the name was not null or empty.
	Assert.That(logic.IsValid(contract), Is.True);

	mocks.VerifyAll();
}

How Do You Send in the Real Service?

The real service just needs to have ICustomerWebService tacked on to the end of it's inheritance declaration. So it ends up looking something like this:

public class CustomerWebService : WebService, ICustomerWebService{
	public CustomerWebService(string url){
		this.Url = url;
	}
	public Customer GetCustomer(int customerID){
		//here we would make the real call to the webservice,
		//instead we'll just send back a new Customer.
		return new Customer();
	}
}

Since that method was the basis for defining the interface in the first place, it's already implementing the interface so we're done with that class. In our code whatever application is using the MyBusinessObject would be responsible for wiring up the real object. So the application code may be code in a server side click event that looks like this:

protected void btnVerifyCustomer_Click(object sender, EventArgs e){
	string url = ConfigurationManager.AppSettings["webserviceurl"];
	ICustomerWebService service = new CustomerWebService(url);
	MyBusinessObject biz = new MyBusinessObject(service);
	Contract contract = Contract.Find(345);
	lblContractValid.Text = biz.IsValid(contract).ToString();
}

And better yet, you could move the code that constructs the service to a helper class like this:

public static class ObjectMother{
	public static ICustomerWebService GetCustomerWebService(){
		string url = ConfigurationManager.AppSettings["webserviceurl"];
		return new CustomerWebService(url);
	}
}

Download

Here's the code

Print | posted on Tuesday, August 21, 2007 2:50 PM

 
Author: , 0000-00-00