Designing a quiz with plain text user stories

April 1, 2008 Comments Off

Over the next couple of posts I thought that I would like to discuss the redevelopment of my hiragana and katakana quiz software. This software originally started out as a command-line Perl script, became a PHP web script, before turning into a Ruby on Rails web application. Unfortunately, although the development language changed and possibilities increased, the overall functionality remained very much trapped in the original design. As I have started in other posts, I have plans for my quiz software, but it remains rather static, so perhaps it would be a good idea to talk about user stories as a way of providing clarity and focus.

I tend to prefer to use development tools that simplify the tasks that I have come to find routine, but perhaps more importantly provide me with a fresh and powerful way of looking at new problems that I need to solve. The latest tool to find me is “user stories”:http://rspec.info/documentation/stories.html with “RSpec”:http://rspec.info. Now, I must say that I was already using RSpec to ‘test’ other Ruby on Rails applications and I think that everyone should try and play around with it for a bit, but user stories — in particular the plain-text flavour of them — are what currently occupy my notebook scribbles.

Let me show a somewhat simplistic and incomplete example of a user story:

Story: starting a quiz
  As a visitor
  I want to start a quiz
  So that I can hopefully learn something

  Scenario: quiz doesn't exist
    Given a quiz named hiragana
    When I start another quiz
    Then I should see the 404 error page
    And I should see a link to the list of quizes

  Scenario: quiz does exist
    Given a quiz named hiragana
    When I start the quiz
    Then I should see the quiz
    And I should see a question

We have a story with two scenarios: when the quiz exists and when the quiz doesn’t exist. It is not very interesting, but at least we know that the user should see an error page if they try and start a quiz that doesn’t exist, and that they should also see a list of quizes that are available instead. If the quiz does exist then they should see a question.

This might be useful if we want to ensure that the 404 page is shown when the requested quiz doesn’t exist, but it is not very satisfactory to write down and seems a bit pointless. It also doesn’t help us understand what happens when a user starts a quiz.

One of the main thing that I like about plain text user stories is that I can easily scribble them down on a piece of paper. I can focus on describing my goals without worrying about the implementation details and afterwards even use this “pseudotestcode” directly. The plain text user story also is nicely separated from the implementation code that is needed to make it work and because of this, it is easy to fit an understandable and detailed story in a manageable page or two.

I think I will use the following story in my quiz redevelopment and next time I will discuss other stories, and also how to test these stories.

Story: starting a quiz
  As a visitor
  I want to start a quiz
  So that I can hopefully learn something

  Scenario: quiz doesn't exist
    Given a quiz named hiragana
    And a quiz named katakana
    When I start another quiz
    Then I should see the 404 error page
    And I should see a list of quizes
    And the list of quizes should have 2 items

  Scenario: default configuration
    Given a quiz named hiragana
    When I start the quiz
    Then I should see the quiz
    And I should see a question
    And I should see the text "Question 1 of 5"
    And there should be 3 textboxes

  Scenario: 10 questions
    Given a quiz named hiragana
    And the quiz has been configured for 10 questions
    When I start the quiz
    Then I should see the quiz
    And I should see "Question 1 of 10"

  Scenario: 5 hiragana per question
    Given a quiz named hiragana
    And the quiz has been configured for 5 hiragana
    When I start the quiz
    Then I should see the quiz
    And there should be 5 textboxes

The perils in trusting syntax highlighting

February 14, 2008 Comments Off

Today after upgrading RSpec I ran a user story that I had just written and received a wonderful error: You have a nil object when you didn't expect it! (NoMethodError). The error occurred while evaluating nil.add_scenario. I didn’t even think to check my user story because the syntax highlighting looked fine and it made sense to me:

  Scenario: areas have different types
    As an administrator
    I want to assign different types to the areas
    So that I can structure the home page

    Scenario: footer shows page links
      Given an area
      And area has 5 pages
      And area is of type footer
      When I view the home page
      Then I should see the home page
      And I should see the footer section
      And the list section should contain 0 areas
      And the footer section should contain 5 links

Of course, if I actually bothered to read it properly then I would have seen that the first line should have read:

  Story: areas have different types

Moral of the story: even if the code that you have to write is small, always assume that you are capable of making silly mistakes!

an assert_difference that works with arrays

November 23, 2006 Comments Off

I have started using the better assert_difference that was posted at blog.caboo.se to improve my rails tests. This allowed me to write tests such as:

  assert_difference Item, :count do
    Item.create(:name => "Monkey magic")
  end

This replaces:

  item_count = Item.count
  Item.create(:name => "Monkey magic")
  assert_difference, item_count + 1, Item.count

but I also wanted to be able to test if emails were sent by writing:

  assert_difference ActionMailer::Base.deliveries, :size do
    SystemNotifier.deliver_important_message
  end

That didn’t work and it was noted on that website that the code didn’t work properly with arrays so I have modified the assert_difference method to check if the objects respond to the method (e.g. size) and act accordingly:

  def assert_difference(objects, method = nil, difference = 1)
    initial_values = []
    if objects.respond_to? method
      initial_values = objects.send(method)
    else
      objects = [objects].flatten
      initial_values = objects.inject([]) { |sum,obj| sum << obj.send(method) }
    end

    yield

    if difference.nil?
      objects.each_with_index { |obj,i|
        assert_not_equal initial_values[i], obj.send(method) #, "#{obj}##{method}"
      }
    else
      if objects.respond_to? method
        assert_equal initial_values + difference, objects.send(method) #, "#{objects}##{method}"
      else
        objects.each_with_index { |obj,i|
          assert_equal initial_values[i] + difference, obj.send(method) #, "#{obj}##{method}"
        }
      end
    end
  end

I have tested it for an array such as ActionMailer::Base.deliveries but I haven't tested it in other situations so hopefully it won't create any problems.


The rspec version of this is:

  def assert_difference(objects, method = nil, difference = 1)
    initial_values = []
    if objects.respond_to? method
      initial_values = objects.send(method)
    else
      objects = [objects].flatten
      initial_values = objects.inject([]) { |sum,obj| sum << obj.send(method) }
    end

    yield

    if difference.nil?
      objects.each_with_index { |obj,i|
        initial_values[i].should_not_equal obj.send(method) #, "#{obj}##{method}"
      }
    else
      if objects.respond_to? method
        (initial_values + difference).should_eql objects.send(method) #, "#{objects}##{method}"
      else
        objects.each_with_index { |obj,i|
          (initial_values[i] + difference).should_eql obj.send(method) #, "#{obj}##{method}"
        }
      end
    end
  end


Flags…