Developers reach deadlines faster if they skip TDD in the same way that skydivers reach the ground faster if they skip parachutes.
Increasing Velocity
8
07
A Quick Design Session
25
06
The team has identified the tasks for the first iteration and are about ready to go to work on it. Before we continue, we pause to have a quick design session where the major classes of the application are identified. The design session is done collaboratively with several, if not all, team members present.
For this session we elect to use CRC-cards (Class, Responsibility, and Collaboration). We walk through the scenario that we are about to implement and try to find the major classes involved. Where possible we try to use terminology from the system metaphor that we decided to use for describing the system. We write the name of those classes along with their responsibilities. Finally, we walk through one or two scenarios and try to see how the classes interact and write down the collaborators of the classes on the cards as well.
Looking at the acceptance test for the current story, we end up with the following set of cards:
The team sits around a table with the cards created so far spread out, each time a class is mentioned someone lifts up that card and holds it for everyone to see. If a new class is needed, a new card is created. If a class is no longer needed its card is ripped up and thrown away.
For the scenario of connecting two applications the discussion can go something like this:
- The calling application is started and an instance of
Subscriberis created (The subscriber card is lifted up by one of the participants). - The
Subscribercreates an instance ofCallReceiverand tells it to start listening for calls (both cards are held up). - Here someone notices that we need to store information about how to reach a subscriber somewhere. Probably the IP-address and port where this application receives connections. We make a note of this, but we don’t feel we need to decide exactly what class holds that data at this point.
- The called application is started and the same scenario as above is repeated
- The
Subscriberin the calling application makes a call to the called application by creating aConversationclass (cards in the air). - Someone points out that we need the know how to reach the called application as well. This information needs to be stored somewhere. We make a note of that and move on.
- The
CallReceiverin the called application detects the incoming request and creates aConversationclass that will match the one on the calling side. TheConversationis passed along to theSubscriberclass, which in turn notifies theConsoleclass about the new conversation. - The
Subscriberclass in the calling application notifies itsConsoleabout the newConversation - In both applications the
Consoleclass displays a message that indicates that we have a conversation started.
After the team has been through this a few more times, should have enough information to start implementing the design. At any time the design can be revisited if deemed necessary.
Highlights
- Design is a collaborative effort. Everybody on the team is involved in the discussions and walk away with a pretty good understanding of the design.
- This lessens the need for some of the intra-team documentation needed. If every developer designs her part of the application in isolation, there probably will be a greater need for documentation and formal reviews in order to get input and spread knowledge. The argument for dividing the design up usually is that of efficiency. You can cover more ground if you work in parallel. I believe that the raise in cost of communication that follows will hurt you in the end though.
- The level of the design artifact is at a level that all of the team members can understand and comment on. Developers, customers, technical editors, and QA people, etc.
- Using cards, ripping them up, moving them around, and pointing to them during discussions is a more tactile approach than working in a UML-drawing tool or even drawing on a whiteboard. I’d think this is a positive thing for the overall understanding in the team. In design sessions where the whiteboard is used directly without cards, there is not room for more than one or two people up at the board at the time. This can lead to one or two of the stronger wills in the room to high jacking the meeting, not leaving enough room for everyone to air their opinions.
- This does not replace the use of UML diagrams or other forms of formal documentation and reviewing, if those are necessary.
- The session aims to create a design that is suitable for the stories at hand. We will refine that design as new stories are planned. If we don’t have enough buy in from the organization that this is possible. These kind of exercises will most probably fail.
Let’s start implementing…
The First Iteration Plan
23
06
After some time off I return to our XP simulation where I attempt to implement a little messaging application. For the iteration ahead there is only one story planned: “A user can send a text message to another user - the console version.” The story is estimated at 5 points. We have a good idea of what this story entails as we have an acceptance test that describes the details.
We begin by planning the iteration by breaking down the story that is up on the white board into tasks for the developers. We estimate those tasks as best we can. When estimating the tasks we don’t use an abstract notion like story points; a simple time estimate will do. None of the tasks should take a lot of time to do, it should be a matter of hours or, at maximum a day.
When we created the user stories we did our best to ignore dependencies. When we plan the tasks for the iteration, we allow ourselves to acknowledge that there is an order to how certain things ought to be implemented. It does make sense to make two applications connect to each other before we attack sending messages between them.
This is the first iteration so we don’t have a good idea of how much we can accomplish in one iteration. My gut feeling tells me that we probably could squeeze more into one iteration, but let’s wait with that until we have marked some tasks as “done” and have a better grip on our velocity. History has proven me to sometimes be too optimistic.
Here’s the project white board after we’ve done the iteration plan:
The next thing to attack: connecting two applications.
Unit Testing and Threads
19
06
While working on the IM application, a pattern seems to be emerging for the tests that I write. I have an object that is supposed to wait for something to happen, and when that something occurs that object is expected to notify another object by making a call to it. For instance, I have a class that waits for something to be sent over a socket, and signal a client when it has received what it needs. Waiting for something to happen on a socket means that the current thread cannot do anything else while waiting - that operation needs to be done on a worker thread.
When I write a test for that type of operation it follows a pattern similar to:
- Create a test double that we expect to be notified by the class under test.
- Call the CUT and pass it the test double
- The CUT starts a worker thread, which waits for the event to occur and calls back when it does
- The call to the CUT returns to the test code before the callback to the test double has been made
- The test code does whatever it needs to make the CUT notice that it needs to notify the test double
- The test code waits for the test double to be called
- The CUT makes the call to the test double that in turn signals to the test code that the operation has finished
- The test code makes whatever assertions it need to make sure that the test has passed
I ended up creating a class, SignallingWrapper that wraps all of this behavior. Using it for a test like the one above looks something like this
def test_with_threads
testDouble = create_test_double
wrapper = SignallingWrapper.new(testDouble, ["method_on_test_double"])
wrapper.call_in_context do
object_under_test.method_to_test(wrapper)
wrapper.wait_for("method_on_test_double")
end
assert(testDouble.didItWork?)
end
In the code above we create a SignallingWrapper around the test double, passing along the name of the method that we expect the CUT to call back. We then call the CUT inside a test context, which makes sure that thread synchronization works properly. After that we wait for the method on the test double to be called properly. Finally, we assert whatever is necessary to complete the test.
You can specify, and wait for, more than one method on the test double.
The SignallingWrapper seems to do the trick for me. It may very well be the case that I really should refactor the tests so that threading is not an issue, but I don’t see a clean way to do that at the moment. In any way I had a blast implementing this. It introduced me to concepts like object-specific classes and dynamically defining methods. Ruby contains a lot of stuff. Perhaps too much…
You can take a look at SignallingWrapper here.
GoFixture – Waiting for FitLibrary for Ruby
2
06
I have not been able to find a port of FitLibrary for RubyFit. I especially miss the functionality of DoFixture where you can, amongst other things, write fixture tables that contain rows like this
| user | Alice | logs on to server | AliceServer |
which I find really easy to read.
DoFixture interpret the first and every second cell after that as a name of a method to call and the rest of the cells are interpreted as arguments to that method. So the table row above would be translated into a call to the method similar to
public bool userLogsOnToServer(String user, String Server) {...}
where “Alice” would be passed as the first parameter and “AliceServer” would be passed as the second parameter.
While waiting for this to hit Ruby (if it already has, please let me know), I wrote GoFixture, which looks kind of like an ActionFixture that can call methods DoFixture style.
A table like this
| MyGoFixture | |||
| user | Alice | logs on to server | AliceServer |
can be backed up by the following class
require 'go_fixture'
class MyGoFixture < GoFixture
def user_logs_on_to_server(user, server)
...
end
end
It seems to work with RubyFit 1.1. If you want to run its unit tests you need FlexMock 0.3.2
You can download GoFixture here.
Tag-Team Programming
18
06
A while back I got the opportunity to do some coding together with a colleague of mine that works in North Andover. I am situated in Malmö, Sweden, myself so we had a time difference of six hours to overcome. Using tools like NUnit and FIT/Fitnesse we were able to get quite a good flow even if the circumstances under which we had to work were far from optimal.
A somewhat idealized day could look something like this for me:
When I got in to work I got the latest source, built it, and ran the suite of acceptance tests that we had created earlier. By doing that I got a really good overview of what had happened while I was away from the office. The currently failing acceptance test would act as a marker that showed me how far we were into development. I could also quite easily see the progress since the last day. What new tests were passing that weren’t passing yesterday? Were there any changes to the acceptance tests themselves because my colleague had found some changes in the way things ought to work?
When I had reviewed the changes to the acceptance tests, I could take a look at the NUnit tests that had been added or modified during the night. This gave me a good view of what classes had been modified, and in what way. The unit tests acted very well as more low level documentation on what behavior had been worked on during the night. Now I could do a review of the changes made and see if I could see some refactorings that would make things a bit easier to read and make changes to.
Next, I would turn to the currently failing acceptance test. By starting to look at the code that performed the actual test, I could quite easily get a feel for what needed to be done next - make the acceptance test go a little bit further. I could now go on and start developing the next step, using TDD, and hopefully I could make a complete acceptance test pass.
Around 3 pm CET, my colleague would get to work and we could talk a bit on the phone, or using mail, or IM. During this time we could have a quick design session or discuss some other issues that had come up. After that I could go home, and my colleague could take over for the night.
What struck me when doing this was how much communication the tests were able to facilitate, both the acceptance tests and the unit tests. When the other means of communication where crippled, the tests could make up for a lot more than I had expected.
The acceptance tests:
- Provided a progress bar of sorts on how we were doing.
- Gave enough details on what had been done so one could get a quick overview of what had been done since last time.
- Gave a starting point for today’s work. Start implementing the currently failing feature.
The unit tests:
- Provided the details on what had been done since last.
- Provided low level documentation on any new classes that had been created since last.
When most other forms of communication were limited, it dawned on me how much about communication testing really is. Yes, testing is about asserting that your code behaves properly. Yes, TDD is very much a way to get a good, loosely coupled design. But it is also very much about communication. It is therefore important to keep the tests readable and to the point.
I would also like to get the opportunity to try out some long distance pair programming at some point as well, and see how that goes.
Extract Class and Test-Driven Development
15
06
The mantra of TDD goes “Red, Green, Refactor!” That is, you write a test that fails, you make that test pass, and then you refactor to remove duplication and other code smells.
It’s a good idea to keep in mind where in the TDD groove one is at each moment, even if the time spent in each phase may be as short as a few seconds. When one has a failing test, one shouldn’t be refactoring. When one is refactoring, one shouldn’t write new tests that fail. This is a good rule to follow, and I always try to follow it meticulously.
One refactoring always make me kind of lost when trying to adhere to the above rule: Extract Class. Most refactorings only touch the internals of whatever class is currently being TDD’ed. Refactorings don’t change the behavior of a class, and should therefore not need new tests. In one sense this is true for Extract Class as well; it does not change the behavior of the original class. But it does, however, create a new class, with it’s own behavior that needs to be tested.
The common way to handle this seems to be to just let things be and let the tests that assert the behavior of the original class indirectly test the behavior of the extracted class. In most scenarios this is probably ok, but the fact that you cannot remove the dependency from the original class to the extracted class without losing test coverage kind of bugs me.
After some discussions about this on the TDD list some embryos for best practices emerged.
- When one is using the original tests to assert the behavior of the extracted class, it may be a good thing to make the extracted class a nested private class of the original class. At least as an intermediate step. This tells the world that the marriage between the two behaviors has not been completely dissolved yet.
- As part of extracting the class, take time to consider what the behavior is that one is pulling out of the original class. Then, look for tests that cover that behavior, and see if it is possible to pull those tests out to a different test fixture. Perhaps after some further refactoring of the tests. Initially, let the new test fixture test the original class. After the refactoring is performed, let it test the extracted class.
- Carl Manaster suggested that triangulation be used when extracting the class. I think this can be used as part of refactoring the tests as well.




Filed under:
Recent Blog Entries
