Microsoft's Internet Explorer browser has no built-in vector graphics machinery required for "loss-free" gradient background themes.

Please upgrade to a better browser such as Firefox, Opera, Chrome, Safari or others with built-in vector graphics machinery and much more. (Learn more or post questions or comments at the Slide Show (S9) project site. Thanks!)

XReq

Behaviour Driven Development for Ada

Plan

  1. Behaviour Driven Development
  2. XReq — Simple Example
  3. XReq — Advanced Techniques
  4. Adapting XReq

Behaviour Driven Development

Principle:

  • Write specifications first
  • Execute specification tests and check that the test fail
  • Determine the parts of the code that will need to be modified to implement the feature

Executable Specification

The behaviour driven development has a weakness, the specification tests must be in sync with the specification itself.

That's why the executable specifications were invented.

Executable Specification — Example

File features/calculate.feature:

Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Scenario: Add two numbers
    Given the calculator has just been powered on
    When  I press the button "1"
    And   I press the button "+"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "3" on the screen

  Scenario: Subtract two numbers
    ...

Make the Computer Understand English

In order to execute the specification, you have to translate the English text for the computer.

Write the package features/step_definitions/steps.ads:
(XReq can generate a stub)

with XReqLib.General; use XReqLib.General;

package Steps is

   --  @given ^the calculator has just been powered on$
   procedure Given_the_calculator_has_just_been_powered_on (Args : in out Arg_Type);

   --  @when  ^I press the button "([^"]*)"$
   procedure When_I_press_the_button (Args : in out Arg_Type);

   --  @then  ^I should see "([^"]*)" on the screen$
   procedure Then_I_should_see (Args : in out Arg_Type);

end Steps;

Executable Specification — Stub

Write the package features/step_definitions/steps.ads:

package body Steps is

   procedure Given_the_calculator_has_just_been_powered_on (Args : in out Arg_Type) is
      Not_Yet_Implemented : exception;
   begin
      raise Not_Yet_Implemented
         with "Procedure " & "Given_the_calculator_has_just_been_powered_on" & " not implemented";
   end Given_the_calculator_has_just_been_powered_on;

   procedure Then_I_should_see_on_the_screen (Args : in out Arg_Type) is
      Not_Yet_Implemented : exception;
   begin
      raise Not_Yet_Implemented
         with "Procedure " & "Then_I_should_see_on_the_screen" & " not implemented";
   end Then_I_should_see_on_the_screen;

   ...

end Steps;

Stub generated using: xreq --fill-steps-in steps features/calculate.feature

How does step definition works

Every time a step is executed (Given, When or Then), the corresponding function is executed. A data structure is given as parameter to allow accessing specific values of the step (Example: which key to press).

Args.Match (N) ret String Get the Nth match in the regular expression
Args.First/Last_Match ret Natural First and last index to give to Match
Args.Text ret String Get the text block associated with the step (see later to see how to specify a text block)
Args.Table ret Table_Type Get the tabular structured data associated with the step (see later to see how to specify a table)

Table.Item (X, Y) ret String Get the table element at coordinates (X, Y)
Table.First/Last_X/Y ret Integer First and last index for the X and Y axis in the table

Executable Specification — How to execute

Executing using XReq:

Execution Sample

$ xreq -x test_suite -m features/calculate.feature
$ features/tests/test_suite
Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Scenario: Add two numbers
    Given the calculator has just been powered on
      STEPS.GIVEN_THE_CALCULATOR_HAS_JUST_BEEN_POWERED_ON.NOT_YET_IMPLEMENTED: Procedure Given_the_calculator_has_just_been_powered_on not implemented
    When I press the button "1"
    And I press the button "+"
    And I press the button "2"
    And I press the button "="
    Then I should see "3" on the screen

1 scenario (1 failed)
6 steps (1 failed, 5 skipped)
Finished in 0s

There is an error because we haven't yet implemented the calculator

Implementing the Calculator

The first thing we did is to write the specification.

Then we have to implement the feature:

Calculator Implementation

Assume we wrote the calculator using calculator.ads:

package Calculator is

   type Button_Type is
     (Button_0, Button_1, Button_2, Button_3, Button_4, Button_5, Button_6,
      Button_7, Button_8, Button_9, Button_Add, Button_Sub, Button_Mult,
      Button_Div, Button_Equal);

   procedure Reset;
   procedure Press (Button : in Button_Type);
   function  Screen return String;

end Calculator;

Step Definitions Implementation

Let's fill the stub features/step_definitions/steps.ads:

with Calculator;

package body Steps is

   --  @given ^the calculator has just been powered on$
   procedure Given_the_calculator_has_just_been_powered_on (Args : in out Arg_Type) is
   begin
      Calculator.Reset;
   end Given_the_calculator_has_just_been_powered_on;

   --  @when  ^I press the button "([^"]*)"$
   procedure When_I_press_the_button (Args : in out Arg_Type) is
      Error    : exception;
      Button   : Calculator.Button_Type;
      Btn_Name : constant String := Args.Match (1);
   begin
      Assert (Btn_Name'Length = 1,
              "Button name has wrong number of characters");
      case Btn_Name (0) is
         when '+' => Button := Calculator.Button_Add;
         when '-' => Button := Calculator.Button_Sub;
         when '*' => Button := Calculator.Button_Mult;
         when '/' => Button := Calculator.Button_Div;
         when '=' => Button := Calculator.Button_Equal;
         when '0' .. '9' =>
            Button := Calculator.Button_Type'Val (Btn_Name (0) - '0');
         when others =>
            Assert (False, "Unknown button name " & Btn_Name);
      end case;
      Calculator.Press (Button);
   end When_I_press_the_button;

   --  @then  ^I should see "([^"]*)" on the screen$
   procedure Then_I_should_see_on_the_screen (Args : in out Arg_Type) is
   begin
      Assert (Calculator.Screen = Args.Match (1),
              "The screen shows " & Calculator.Screen & " instead of " & Args.Match (1));
   end Then_I_should_see_on_the_screen;

end Steps;

Execution Sample — Passing Scenario

$ xreq -x test_suite -m features/calculate.feature
$ features/tests/test_suite
Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Scenario: Add two numbers
    Given the calculator has just been powered on
    When I press the button "1"
    And I press the button "+"
    And I press the button "2"
    And I press the button "="
    Then I should see "3" on the screen

1 scenario (1 passed)
6 steps (6 passed)
Finished in 0s

Advanced Techniques — Background (1)

Many scenarios might need to share common initialization steps (Given)

Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Scenario: Add two numbers
    Given the calculator has just been powered on
    When  I press the button "1"
    And   I press the button "+"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "3" on the screen

  Scenario: Subtract two numbers
    Given the calculator has just been powered on
    When  I press the button "1"
    And   I press the button "-"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "-1" on the screen

Advanced Techniques — Background (2)

Se we add a Background.

Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Background:
    Given the calculator has just been powered on

  Scenario: Add two numbers
    When  I press the button "1"
    And   I press the button "+"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "3" on the screen

  Scenario: Subtract two numbers
    When  I press the button "1"
    And   I press the button "-"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "-1" on the screen

Advanced Techniques — Outlines (1)

If many scenarios have the same steps with different values ...

Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Background:
    Given the calculator has just been powered on

  Scenario: Add two numbers
    When  I press the button "1"
    And   I press the button "+"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "3" on the screen

  Scenario: Subtract two numbers
    When  I press the button "1"
    And   I press the button "-"
    And   I press the button "2"
    And   I press the button "="
    Then  I should see "-1" on the screen

Advanced Techniques — Outlines (2)

... we can create Scenario Outlines:

Feature: Calculate
  In order to do simple calculations
  As a user
  I want to be able to do simple operations (add, subtract, multiply and divide)

  Background:
    Given the calculator has just been powered on

  Scenario Outline: Operation on two numbers
    When  I press the button "<op1>"
    And   I press the button "<op>"
    And   I press the button "<op2>"
    And   I press the button "="
    Then  I should see "<res>" on the screen

    Examples:
      | op1 | op  | op2 | res |
      |  1  |  +  |  2  |  3  |
      |  1  |  -  |  2  | -1  |
      |  1  |  *  |  2  |  2  |
      |  4  |  /  |  2  |  2  |

Advanced Techniques — Text Blocks

It is possible to provide long text blocks to the steps:

Feature: Check filesystem
  In order to check my filesystem
  As a system administrator
  I want to verify the content of /etc/fstab

  Scenario: Verify /etc/fstab
    Given The system just finished its installation
    When I open the file "/etc/fstab"
    Then I should see
      """
      /dev/sda1 /        ext4   defaults       1 1
      /dev/sda2 /home    ext4   defaults       1 2
      /dev/sda3 swap     swap   defaults       0 0
      devpts    /dev/pts devpts gid=5,mode=620 0 0
      sysfs     /sys     sysfs  defaults       0 0
      proc      /proc    proc   defaults       0 0
      """

Advanced Techniques — Tables

It is also possible to provide structured data in the form of tables:

Feature: Check network
  In order to check my network configuration
  As a system administrator
  I want to verify the hosts of my local network

  Scenario: Verify hosts
    Given The system is plugged on the network
    When I explore the local network
    Then I should see the following hosts
      | Interface | Protocol | Hostname     | MAC Address       |
      | wlan0     | IPv4     | meryl        | 1c:4b:d6:82:76:d6 |
      | wlan0     | IPv4     | sogilis01    | 00:1c:23:b3:b4:2e |
      | wlan0     | IPv4     | compta       | 00:11:2f:51:8a:d5 |
      | wlan0     | IPv4     | kylae        | 00:19:e3:d8:01:8b |
      | wlan0     | IPv4     | virtualbuntu | 08:00:27:b6:7e:cb |

XReq Features

Thank You

Any Questions ?