Search >>

RSS   COM     HOME

>    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…






>  Leave a Reply