TDD in the eyes of a simpleminded: Part 2 - Testing inner behavior
code:
If you haven’t read about the NameResolver at my previous post(TDD: Part1), now is the time as I’m going to take this class and use it later on in this post.
We are ready to take the next step and talk about “playing a doctor”. Imagine this picture: you are a doctor looking for a gun’s bullet in one of your patient’s stomach. He was shot a few moments ago. “One bullet to the chest” the nurse inform you. It’s all messy and you can’t find the bullet. You need to clear the blood, move the organs, going micro-level on the details so you don’t hit a vein on the way. Every mistake is a “welcome-better-place” for your patient and you don’t want this on your conscience do you ?
While developing applications, we are facing a world of “playing doctors”. We need to change inner behaviors in our code or others code. We want the application to live the surgery and we realize that the smallest change in a “core method” can make the application bleed all over the place without even noticing it. Every click on the keyboard raises our heart rate. But how can we check inner code inside a method? How can we get a micro-level look at the method’s internals, at the stomach of our tested object ?
This is what I’m going to address at this post. But before we see solutions, let’s talk about the client’s requirements so we’ll have something to play with.
Client’s requirements v1.0:
A class named HotelReservationService is required. In it, a method named ReserveRoomForTwo that receive the couple’s name in the format “[man] and [wife] [family name]”. The method will reserve the room and name the room under the family’s name. The method will return ReservationInformation object with the details (room, floor number & status).
- If the received couple’s name is empty (“”) – Throw an exception with “The couple name must be supplied”.
- If the hotel don’t have available rooms, the returned ReservationInformation should hold a status of “NoRoomAvailable”.
- if the everything goes well, the method should return a ReservationInformation filled with the floor number, room number, reservedFor (filled with the name of the family) and status of “RoomReserved”.
Simple solution, without TDD:
public class HotelReservationsService
{
public ReservationInformation ReserveRoomForTwo(string coupleName)
{
// …. some checks …
NameResolver resolver = new NameResolver();
string familyName = resolver.GetFamilyName(coupleName);
HotelRepository repository = new HotelRepository();
return repository.ReserveRoom(2, familyName); // 2 = two people
}
}
As you can notice, we have 2 dependencies here: NameResolver and HotelRepository. We need to test ReserveRoomForTwo but how can we do it ? the results of our tests for this method are coupled with the implementation of NameResolver and HotelRepository. If they have bugs in it, it will certainly affect our tests and therefore making them *unreliable*. What’s the point in writing tests if you doubt that a green(successful) result might actually be red(fail) under the surface due to an inner bug in one of the dependencies?
Keeping with my metaphor, the dependencies are the blood and organs laying around and making it harder for you to find the bullet, may it be a bug or a new feature. You need to clear(isolate) the area so things will be easier to see. You need a green light you can actually *trust*.
We’ll need to use Dependency Inversion Principle(DIP) to isolate our tested class from the actual implementation of its dependencies.
Our goal:
Inject dummy object instead of the dependencies and make them “act like” everything is good\bad according to our need. Will use DIP to make it happen.
We don’t want to test NameResolver nor HotelRepository. We assume that they are bugs-free. We need to test one and only one class at a time. Our goal is to test the interaction between our tested class to its dependencies.
Let’s use TDD to solve the client’s requirements:
Say hello to your new friend: geek, this is Stub; Stub, this is geek. Come on, don’t be shy, give him a hug, he will serve you well in time. Feel nice and fuzzy ? great, now let’s TDD this baby. Oh, and don’t worry, if you don’t understand the concept of “Stub” yet, everything will be clearer in a few moments. Just think about Stub as a dummy object.
We start with? Come on! I want to hear it from you! we’ll start with a Create method!
[TestFixture]
public class HotelReservationsServiceTests
{
[Test]
public void Create()
{
HotelReservationsService service = new HotelReservationsService();
Assert.IsNotNull(service);
}
}
As usual, in order to compile this method we’ll need to create an empty class:
public class HotelReservationsService
{
}
Let’s run the test – 1 passed, 0 failed. Good.
Let’s write our second test and look to our requirements:
[Test]
[ExpectedException(typeof(ArgumentException), “The couple name must be supplied”)]
public void ReserveRoomForTwo_EmptyCoupleName_ThrowEmptyCoupleNameException()
{
HotelReservationsService service = new HotelReservationsService();
ReservationInformation info = service.ReserveRoomForTwo(string.Empty);
// We’ve got nothing to assert. We expect an exception to raise.
}
This will not compile as we don’t have ReserveRoomForTwo method nor ReservationInformation class. Let’s write them down:
public class ReservationInformation
{
private int m_roomNumber;
private int m_floorNumber;
private string m_reservedFor;
private string m_status;
public ReservationInformation(string reservedFor, int floorNumber, int roomNumber, string status)
{
ReservedFor = reservedFor;
FloorNumber = floorNumber;
RoomNumber = roomNumber;
Status = status;
}
public string ReservedFor
{
… get & set …
}
public int FloorNumber
{
… get & set …
}
public int RoomNumber
{
… get & set …
}
public string Status
{
… get & set …
}
}
Now let’s add the ReserveRoomForTwo method to our HotelReservationsService:
public ReservationInformation ReserveRoomForTwo(string coupleName)
{
throw new NotImplementedException(“”);
}
Compile. Run tests – 1 passed, 1 failed. Our test expect for ArgumentException with a specific message but we throw NotImplementedException. Let’s fix it.
public ReservationInformation ReserveRoomForTwo(string coupleName)
{
throw new ArgumentException(“The couple name must be supplied”);
}
Run tests – 2 passed, 0 failed. Good.
Time for refactoring our tests as we create the HotelReservationsService in two methods. This time, instead of creating a “FactoryMethod” named GetNewHotelReservationsServer like we did in our NameResolver tests, let’s use the [Setup] attribute.
[TestFixture]
public class HotelReservationsServiceTests
{
HotelReservationsService service;
[SetUp]
public void SetupTest()
{
service = new HotelReservationsService();
}
[Test]
public void Create()
{
Assert.IsNotNull(service);
}
[Test]
[ExpectedException(typeof(ArgumentException), “The couple name must be supplied”)]
public void ReserveRoomForTwo_EmptyCoupleName_ThrowEmptyCoupleNameException()
{
ReservationInformation info = service.ReserveRoomForTwo(string.Empty);
// We’ve got nothing to assert. We expect an exception to raise.
}
}
Let’s continue to check our requirements: We want to represent a case where the provided couple’s name is good but the hotel is full.
[Test]
public void ReserveRoomForTwo_HotelIsFull_ReservationInformationWithStatusNoRoomAvailable()
{
// We create a dummy repository and set the values we want it to return.
FakeHotelRepository repository = new FakeHotelRepository();
repository.StatusToReturn = “NoRoomAvailable”;
service.SetRepository(repository); // Important! set the fake repository in the service.
string goodCoupleName = “man and woman FamilyName”;
ReservationInformation info = service.ReserveRoomForTwo(goodCoupleName);
Assert.AreEqual(“NoRoomAvailable”, info.Status);
}
Run tests – 2 passed, 1 failed(our last one).
We get an exception here which is obvious (we hard-coded “throw new ArgumentException…”) instead of a status “NoRoomAvailable”. Notice that we are create a FakeHotelRepository and injecting it to our tested class. The purpose is to create a repository which will always return “NoRoomAvailable”. We want to test the *interaction* between our ReserveRoomForTwo method in our tested object to the repository. This can look bizarre but let’s remember that we want to test only our ReserveRoomForTwo method and not the repository class.
Let’s make our test green. minimum effort.
We need some sort of HotelRepository in order to know if the hotel is full. At the moment, we don’t need the NameResolver as we are looking for the status only. Let’s define an interface named IHotelRepository.
public interface IHotelRepository
{
ReservationInformation ReserveRoom(int capacity, string familyName);
}
This time we need to build the “basics” so the minimum effort will be a little more than we use to, but that’s OK as we’ll do it only once. Let’s add this code to our HotelReservationsService class:
private IHotelRepository m_repository;
private IHotelRepository Repostiory
{
get
{
if (m_repository == null)
throw new ArgumentNullException(“Repostiory”, “Repository must be set before being used”);
return m_repository;
}
set { m_repository = value;}
}
}
public void SetRepository(IHotelRepository repository)
{
Repostiory = repository;
}
Now we can use the repository in our ReserveRoomForTwo method:
public ReservationInformation ReserveRoomForTwo(string coupleName)
{
if (coupleName == string.Empty)
throw new ArgumentException(“The couple name must be supplied”);
else
{
ReservationInformation info = this.Repostiory.ReserveRoom(2, coupleName);
return info; // just so we’ll remember that we return ReservationInformation.
}
}
We call the repository and ask it to save a room for the received couple. All we left with is to create the FakeHotelRepository object:
internal class FakeHotelRepository : IHotelRepository
{
public string ReservedForToReturn = “”;
public int FloorNumberToReturn = 0;
public int RoomNumberToReturn = 0;
public string StatusToReturn = “”;
public ReservationInformation ReserveRoom(int capacity, string familyName)
{
return new ReservationInformation(ReservedForToReturn, FloorNumberToReturn, RoomNumberToReturn, StatusToReturn);
}
}
This class will sit in the tests project and will be used for testing only. Notice that we can manipulate the data we want to return from this repository. Remember: we want to test the interaction between the classes. We assume that the repository in production was tested and will be able to “know” if the hotel is really full or not. We make a fake one so we can control the internal behavior. We are playing doctors: Assuming that the repository is OK, will our class still behave as we expect ?
We can play with the repository and look at the results we get. Will do the same with the NameResolver later on.
Run the tests – 3 passed, 0 failed. So far, requirements 1 and 2 are set.
What is Stub then ?
stub is an object that helps you test another object. You never ever Assert the stub itself.
You use it only as an helper while you’re looking to test another object.
In our example, the FakeHotelRepository is a classic stub. We use it to assert another object – ReservationInformation.
Let’s take a pause here as this post cover a lot of material and code. The next post will answer the third requirement and I’ll present additional solution for the problems we encounter here.