Search >>

RSS   COM     HOME

Post   A Quick Design Session

Jul
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:

CRC Cards iteration one

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 Subscriber is created (The subscriber card is lifted up by one of the participants).
  • The Subscriber creates an instance of CallReceiver and 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 Subscriber in the calling application makes a call to the called application by creating a Conversation class (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 CallReceiver in the called application detects the incoming request and creates a Conversation class that will match the one on the calling side. The Conversation is passed along to the Subscriber class, which in turn notifies the Console class about the new conversation.
  • The Subscriber class in the calling application notifies its Console about the new Conversation
  • In both applications the Console class 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…



Post   The First Iteration Plan

Jul
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:

White Board 2 - First Iteration Plan

The next thing to attack: connecting two applications.



Post   Unit Testing and Threads

Jul
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:

  1. Create a test double that we expect to be notified by the class under test.
  2. Call the CUT and pass it the test double
  3. The CUT starts a worker thread, which waits for the event to occur and calls back when it does
  4. The call to the CUT returns to the test code before the callback to the test double has been made
  5. The test code does whatever it needs to make the CUT notice that it needs to notify the test double
  6. The test code waits for the test double to be called
  7. The CUT makes the call to the test double that in turn signals to the test code that the operation has finished
  8. 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.



Post   The First Story

Jul
6
06

After the first iteration I have learned quite a few things about Ruby basics and the support for what I need to get started developing the application. Looking at the project whiteboard we now see that the next thing to do is to tackle the first user story - “A user can send a text message to another user.”

That little piece of information is not enough to start implementing. We need to know a lot more about what the customer wants and how we are going to support that feature. The most common way of describing how a feature is to be implemented is probably by creating a use case.

A use case describes the flow of the system while it solves something for a user. When I first encountered user stories my immediate reaction was “what is the difference between a user story and a use case?” My take on it is that while a user story is a promissory note, a token that tells us that we are going to get to the details of a feature when it is about time for it in the plan, a use case is a way to actually document those details. In addition user stories are a tool for planning a project. They have a cost attached to them. They can be split and combined. Use cases don’t have these abilities.

We also need a way to verify that a feature works properly. For this we need to specify a test that needs to be passed in order for us to consider the feature implemented. More often than not (at least in my experience) there is great similarity between a test case and a use case. With the test case being a concrete example of the use case. Where the use case specifies an actor “first time customer”, the test case specifies a user “Alice”, whose “user profile does not contain any information about a previous visit to the system under test.”

The test can tell us when we have completed the implementation of a feature. That is information that we want feedback on as early as possible. Even so, my experience is that test cases are often specified after the feature is implemented, when it is time for the dreaded system test. Often the test case is created by someone who is isolated from the rest of the team. In some organizations there seem to be a perceived advantage to have an almost antagonistic relationship between testers and developers in particular. I am not sure that I understand why.

If we detail the user story as a test before we implement it, we might remove some of the waste that it means to document the same thing in both use case format and test case format. We also have an opportunity to get immediate feedback on whether we can consider the feature we are working on finished or not. Developing against a pre-specified test case will give feedback on whether the test case makes sense as well. All of this is feedback we want long before we deploy or go into system test.

Having the possibility to run the test cases automatically by just pressing a button, running a script, or just have them run every time someone checks in files to the version control system makes it really easy to make sure that a feature still works after additional changes have been made to the system. Fit and Fitnesse enables us to specify tests in a way that is readable to non-programmers while they at the same time can be executed automatically. We will use Fitnesse to create test cases in this project.

Discussing the Details

With that in mind, the customer, the developers, and quite possibly a tester sits down and tries to flesh out the details of how our application is to enable users to send messages to each other. We begin with the main scenario. No communication errors or timeouts yet. Let’s start simple. The result is a Fitnesse test that looks like this:

fitnesse1

This describes how two clients are started. One client is started by the user Alice, and the other by the user Bob. The users exchange some messages and we expect the message history on both clients to contain the messages along with information about who sent them. The tables in the document are interpreted as test fixtures by Fitnesse.

This test should of course be supported by other tests that take care of other scenarios. We should for instance have tests that verify the behavior when errors occur. But let’s stick with this test in this example.

In order for the test to be able to run we need to create some code that takes care of calling the system under test on behalf of our fixture tables. We implement some stub classes and methods so that we can get our test to execute:


require 'go_fixture'
require 'fit/row_fixture'

class EstablishConnection < GoFixture
  def start_client_with_user(client, user)
    return false
  end
end

class StartupResults < Fit::RowFixture
  def query
    return Array.new
  end
end

class Conversation < GoFixture
  def user_sends_message_to(fromUser, message, toUser)
    return false
  end
end

class MessageHistory < Fit::RowFixture
  def query
    return Array.new
  end
end

Running the test gives the following result

fitnesse2

Our assignment for this iteration is basically to get these tests to pass. Ideally, these tests should execute a complete system with no fake data or classes. At the moment I haven’t decided on how to approach this though. I can either start two instances of the application and have them communicate with each other, or I can instantiate and configure the internal classes for both client Alice and client Bob in the test code and have them communicate with each other via a fake communication channel. The former approach is the most realistic and tests the actual system, while the latter is easier to test. Right now I think I am going to start with the latter and then see if I can transform it to the former, all in the name of moving forward. Any input on this is highly appreciated.

Now it’s time to start planning the iteration in more detail…



Post   GoFixture – Waiting for FitLibrary for Ruby

Jul
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.