Go Back

Some TDD in the real world

One of the most common things that I run into when doing TDD with NUnit in the 'real world' is that the ugly truth is dependencies are either hard or impossible to test.

 

Take for example this code snippet where we're calling a webservice a couple times to do some work.

    public class HumanResources

 

    {

        public void FireThisGuy(long employeeId)

        {

            HumanResourcesService service = new HumanResourcesService();

            GetEmployeeRequest request = new GetEmployeeRequest();

            request.EmployeeId=employeeId;

            Employee emp = service.GetEmployee(request).Employee;

            emp.TerminationDate = DateTime.Now;

 

            SetEmployeeRequest setRequest = new SetEmployeeRequest();

            setRequest.Employee = emp;

            service.SetEmployee(setRequest);

        }

    }

 

 

 

 

 

We know it's a bad practice to call webservices in our unit tests for a few reasons. (namely it's slow). So let's refactor a little and remove anything related to the webservices out. I extract method on the get...

       public void FireThisGuy(long employeeId)

        {

            Employee emp = GetEmployeeById(employeeId);

            emp.TerminationDate = DateTime.Now;

 

            HumanResourcesService service = new HumanResourcesService();

            SetEmployeeRequest setRequest = new SetEmployeeRequest();

            setRequest.Employee = emp;

            service.SetEmployee(setRequest);

        }

 

        protected Employee GetEmployeeById(long employeeId)

        {

            Employee emp;

            HumanResourcesService service = new HumanResourcesService();

            GetEmployeeRequest request = new GetEmployeeRequest();

            request.EmployeeId=employeeId;

            emp = service.GetEmployee(request).Employee;

            return emp;

        }

 

notice this wasn't a straight up 'extract to method' on your refactoring tool. I did about 5 small refactorings before i did the extract but just notice the complete extraction of the dependency on the webservice to a new method "GetEmployeeById".

 

now the same for the set:

 

       public void FireThisGuy(long employeeId)

        {

            Employee emp = GetEmployeeById(employeeId);

            emp.TerminationDate = DateTime.Now;

            SetEmployee(emp);

        }

 

        protected void SetEmployee(Employee emp)

        {

            HumanResourcesService service = new HumanResourcesService();

            SetEmployeeRequest setRequest = new SetEmployeeRequest();

            setRequest.Employee = emp;

            service.SetEmployee(setRequest);

        }

 

        protected Employee GetEmployeeById(long employeeId)

        {

            Employee emp;

            HumanResourcesService service = new HumanResourcesService();

            GetEmployeeRequest request = new GetEmployeeRequest();

            request.EmployeeId=employeeId;

            emp = service.GetEmployee(request).Employee;

            return emp;

        }

 

now for my unit test the only thing i really want to test is the "FireThisGuy" logic. so first i would need to inherit from HumanResources and make a testing class that overrides the 2 dependencies i do not want.

    public class HumanResources

    {

        public void FireThisGuy(long employeeId)

        {

            Employee emp = GetEmployeeById(employeeId);

            emp.TerminationDate = DateTime.Now;

            SetEmployee(emp);

        }

 

        protected virtual void SetEmployee(Employee emp)

        {

            HumanResourcesService service = new HumanResourcesService();

            SetEmployeeRequest setRequest = new SetEmployeeRequest();

            setRequest.Employee = emp;

            service.SetEmployee(setRequest);

        }

 

        protected virtual Employee GetEmployeeById(long employeeId)

        {

            Employee emp;

            HumanResourcesService service = new HumanResourcesService();

            GetEmployeeRequest request = new GetEmployeeRequest();

            request.EmployeeId=employeeId;

            emp = service.GetEmployee(request).Employee;

            return emp;

        }

    }

    public class TestingHumanResources : HumanResources

    {

        public Employee GetEmployeeByIdTestingEmployee { get; set; }

        protected override Employee GetEmployeeById(long employeeId)

        {

            return GetEmployeeByIdTestingEmployee;

        }

 

        public Employee SetTestingEmployee { get; set; }

        protected override void SetEmployee(Employee emp)

        {

            SetTestingEmployee = emp;

        }

    }

 

 

 

 

 

 

 

 

 

Now i can actually write a test that validates

  public class HumanResources

    {

        public void FireThisGuy(long employeeId)

        {

            Employee emp = GetEmployeeById(employeeId);

            emp.TerminationDate = DateTime.Now;

            SetEmployee(emp);

        }

 

        protected virtual void SetEmployee(Employee emp)

        {

            HumanResourcesService service = new HumanResourcesService();

            SetEmployeeRequest setRequest = new SetEmployeeRequest();

            setRequest.Employee = emp;

            service.SetEmployee(setRequest);

        }

 

        protected virtual Employee GetEmployeeById(long employeeId)

        {

            Employee emp;

            HumanResourcesService service = new HumanResourcesService();

            GetEmployeeRequest request = new GetEmployeeRequest();

            request.EmployeeId=employeeId;

            emp = service.GetEmployee(request).Employee;

            return emp;

        }

    }

    public class TestingHumanResources : HumanResources

    {

        public Employee GetEmployeeByIdTestingEmployee { get; set; }

        protected override Employee GetEmployeeById(long employeeId)

        {

            return GetEmployeeByIdTestingEmployee;

        }

 

        public Employee SetTestingEmployee { get; set; }

        protected override void SetEmployee(Employee emp)

        {

            SetTestingEmployee = emp;

        }

    }

    [TestFixture]

    public class HumanResourcesTests

    {

        [Test]

        public void FireThisGuy_UserIdIn_FiredEmployeeSaved()

        {

            TestingHumanResources hr = new TestingHumanResources();

            hr.GetEmployeeByIdTestingEmployee = new Employee();

            hr.GetEmployeeByIdTestingEmployee.TerminationDate = null;

 

            hr.FireThisGuy(200);

            Assert.IsNotNull(hr.SetTestingEmployee.TerminationDate);

 

        }

    }

 

 

 

 

 

 

 

 

 

 

 

 

 

This is why im a big fan of object oriented programming and using inheritance to break dependencies. It's a real fast solution to some of the unique dependency issues i fight every day and it is a lot easier to read and debug than tons of interfaces.  This and other examples of seaming and seperation can be found in Michael Feather's book "Working Effectively with Legacy Code"

 

 

 

 

Grab the code here

Comments  10

  • Darren 2/7/2010 12:00:00 AM

    That's cool, thanks for sharing!  The testing class that takes in the Employee for testing as a property is a good idea.

    I know I asked you this before, but since this is another example of it:  How do you feel about doing the same type of process, but with an interface instead of inheritance?  In places like these, I'd probably:

    1.)  Hide the HumanResourcesService behind an IHumanResourcesService interface,

    2.)  Add a private IHumanResourcesService member that is initialized to HumanResourcesService in the constructor with Poor Man's DI.  (I try to stay away from Poor Man's DI, but it can be handy when dealing with legacy code.)

    3.)  Add a constructor that lets me pass in the dependency.

    By going this route, I could test that the fired employee was saved by writing a test against a mock IHumanResourcesService.
  • James Peckham 2/7/2010 12:00:00 AM

    I'm fairly anti interfaces right now except in very specific situations where I know i want to be able to interchange those implementations. The cost of interfaces in code clarity is very high in my opinion. Not everyone can keep up with the delegation... in the debugger for example or through 'find usage/find reference/find declaration'. It's been a team discussion a couple times and maybe some day interfaces will be something we can all agree on. For now concrete classes are easier to read and keep up with and we don't need to swap implementations. YAGNI :)
  • Darren 2/9/2010 12:00:00 AM

    With my team, we've been putting the interfaces in the same file as the implementing class in those cases where there's a 1-to-1 match.  I'll move the interface out into its own file when it has multiple implementations.  It lets others keep their F12 in most cases, and it lets us write tests.

    I think is definitely a place where I think I definitely need the interfaces.  The test is an example as to why:  Your test that the fired employee was saved asserts that the termination date was set, so I could comment out the SetEmployee call that actually saves the employee and the test would still pass.  If it were testing against the interface, it would fail.
  • Ben Coffman 2/9/2010 12:00:00 AM

    What are you saying about co-workers here? hahahaha. Very well worded, the "word smith" is training you well.

    Sorry couldn't resist.
  • James Peckham 2/9/2010 12:00:00 AM

    Yes, Ben, you could take it that way or as me accepting that not everyone is going to learn object oriented programming and geek out on TDD like myself. :) In the end, real people doing real work need to read the code and I'm trying not to be such a zealot. I've seen the error in my ways.
  • James Peckham 2/9/2010 12:00:00 AM

    Darren.
    I agree. The overall design could benefit from using interfaces to mock collaborators. I will probably try that next.
  • Ben Coffman 2/9/2010 12:00:00 AM

    Don't let James fool you he knows all about NUnit Mocks, NMock2, DotNetMock, (these are just the ones he discusses in .NET :) ) and incorperating them with interfaces. I think he is stating their is a certain pay off to making your code as easy as possible to understand, which means trading some "better" functionality for "readability."

    But are mocks really testing if you predefine the values you are expecting to get back? Then you are really not testing your full service right?

    There's a hot disscussion topic.... :)
  • Darren 2/10/2010 12:00:00 AM

    James,

    Don't stop being a zealot, just as I'm starting to become one!
  • James Peckham 2/10/2010 12:00:00 AM

    Nice ben! thanks for the SEO goodness. You're right in me understanding how to use those tools functionally and from a purely academic standpoint. I do want to try advancing further into 'real practice' of mocking and utilizing interfaces to further decouple OUR business rules from our vendor libraries. WHERE APPROPRIATE! This is just good sense to allow us flexibility. Right now the bottom line is delivering a working transaction that is unit tested and within short order to meet regulatory deadlines. Overriding a function call that hits an API/SDK library allows me to stub in that data with a shorter amount of code than creating an interface, mocking the interface, and returning the same stub data. It also is a lot easier to read and when you f12 or follow reference/find usage you hit the right code instead of just an interface prototype. I thought other people might enjoy this knowledge as i've learned it from michael feather's book.

    To your last point ben on the mocking vs real testing against the real web services... I know you've mentioned it on your blog  and there is always a place for those types of integration-level tests (that hit the real database and webservices with real data end to end) HOWEVER, we are talking about unit-level tests and the purpose of these is not to test that the data is right but to test that when the state is specific the behavior does what we expect it to.

    For more on the differences between unit level and integration level tests, I highly recommend reading Agile Testing: A Practical Guide for Testers and Agile Teams . From that book they describe the 4 quadrants of testing. The image with the 4 quadrants of testing on Luu Duong's blog somewhat shows the differences in value between these types of tests. I frankly don't know what he talked about in his blog, but notice quadrant 1 is the purely automated "unit level" or developer tests. Quadrant 2 are the story or gui level tests that your customer team or QA group might be doing. For example, our team does Quick Test Pro against the user interface for Quadrant 2.

    I think any and all testing provides some value and we should pick those which maximize the amount of value we get. Any time we stop a bug from going all the way to QA we've saved effort and therefore money. Stopping a production bug is even more money saved. Additionally the value of unit testing is increased when you consider loosely coupled and flexible code that can be regression tested within seconds to add confidence to a team when making new changes.
  • Chris Leon 3/8/2010 12:00:00 AM

    Here's my version of the code:
Post a comment!
  1. Formatting options