Skip to content

Commit

Permalink
# This is a combination of 6 commits.
Browse files Browse the repository at this point in the history
# The first commit's message is:
Prepare for BDD using Behave

# The 2nd commit message will be skipped:

#	First steps with behave

# The 3rd commit message will be skipped:

#	Prepare a CLI for Behave

# The 4th commit message will be skipped:

#	CLI implemented

# The 5th commit message will be skipped:

#	Documentation draft for BDD.

# The 6th commit message will be skipped:

#	Use some colored boxes
  • Loading branch information
AlexandreDecan committed Jan 29, 2016
1 parent 91f8cdc commit e7e0f4d
Show file tree
Hide file tree
Showing 14 changed files with 550 additions and 16 deletions.
10 changes: 5 additions & 5 deletions .travis.yml
Expand Up @@ -3,13 +3,13 @@ python:
- 3.4
- 3.5
install:
- pip install -r requirements.txt
- pip install coveralls
- pip install coveralls coverage wheel sphinx
- pip install -e .
script:
- coverage run --source sismic -m unittest discover
- cd docs
- make doctest
- cd ..
- cd docs && make doctest && cd ..
- sismic-behave docs/examples/elevator.yaml --features docs/examples/elevator.feature
- sismic-behave docs/examples/microwave.yaml --features docs/examples/microwave.feature
after_success:
- coveralls
deploy:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Expand Up @@ -4,6 +4,7 @@ Changelog
Unreleased
----------

- (Added) Support for behavior-driven-development using Behave.
- (Added) An ``io.text.export_to_tree`` that returns a textual representation of the states.
- (Changed) ``Statechart.rename_to`` does not anymore raise ``KeyError`` but ``exceptions.StatechartError``.

Expand Down
221 changes: 221 additions & 0 deletions docs/behavior.rst
@@ -0,0 +1,221 @@
Behavior-driven development
===========================

This introduction is borrowed from `Behave documentation <http://pythonhosted.org/behave/philosophy.html>`__.

Behavior-driven development (or BDD) is an agile software development technique that encourages collaboration between developers, QA and non-technical or business participants in a software project.
It was originally named in 2003 by Dan North as a response to test-driven development (TDD), including acceptance test or customer test driven development practices as found in extreme programming.
It has evolved over the last few years.

On the “Agile specifications, BDD and Testing eXchange” in November 2009 in London, Dan North gave the following definition of BDD:

BDD is a second-generation, outside–in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology.
It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters.

BDD focuses on obtaining a clear understanding of desired software behavior through discussion with stakeholders.
It extends TDD by writing test cases in a natural language that non-programmers can read.
Behavior-driven developers use their native language in combination with the ubiquitous language of domain-driven design to describe the purpose and benefit of their code.
This allows the developers to focus on why the code should be created, rather than the technical details, and minimizes translation between the technical language in which the code is written and the domain language spoken by the business, users, stakeholders, project management, etc.



The Gherkin language
--------------------

The Gherkin language is a business readable, domain specific language created to support behavior descriptions.
It lets you describe software’s behaviour without detailing how that behaviour is implemented.
Gherkin allows the user to describe a feature or part of a feature with representative examples of expected outcomes.

Like YAML or Python, Gherkin is intented to be a human-readable line-oriented language.

.. code-block:: gherkin
Feature: ls
In order to see the directory structure
As a UNIX user
I need to be able to list the current directory's contents
Scenario: List 2 files in a directory
Given I am in a directory "test"
And I have a file named "foo"
And I have a file named "bar"
When I run "ls"
Then I should get:
"""
bar
foo
"""
With Gherkin, such a feature file can serve both as documentation and as automated tests.
These files should be written using natural language - ideally by the non-technical business participants in the software project.
Feature files serve two purposes – documentation and automated tests.

Using one of the available Gherkin parser, one can easily execute the described scenarios and check the expected outcomes.

.. seealso:: A quite complete overview of the Gherkin language is available `here <http://docs.behat.org/en/v2.5/guides/1.gherkin.html>`__.


BDD and Sismic
--------------

The implementation of the various steps of a scenario into executable code is usually the developer's responsibility.
There are many available tools to easily describe the relationship between the elements of a feature file written in
Gherkin and the pieces of code that implements those elements.

Although it is possible to manually integrate the BDD process with any library or software, Sismic is bundled with a command-line utility that does the job for you.
Sismic allows you to not have to write this code, or to use such tools.
Sismic command-line utility, namely ``sismic-behave``, relies on `Behave <http://pythonhosted.org/behave>`__, a Python library for BDD with full support of Gherkin syntax, to bring BDD to statecharts.

Suppose we wish to define the expected behavior of our running example, the elevator.
We first create a feature file that contains several scenarios of interest.
By convention, this file has the extension *.feature* but it is not mandatory.

.. literalinclude:: examples/elevator.feature
:language: gherkin

Let us save this file as *elevator.feature* in the same directory than the definition of our statechart, *elevator.yaml*.

.. note:: As the command-line utility allows to specify the path to each file, it is not mandatory to put all of them
in a single directory.

We then ask Sismic to run the scenarios described in this feature file, using our statechart.

.. code::
sismic-behave elevator.yaml --features elevator.feature
Running this command will translate given feature file into executable code, compute the outcomes of the scenarios and check whether they match what is expected.
The command then displays the set of executed scenarios, the encountered errors, and a short summary.

.. code::
[...]
1 feature passed, 0 failed, 0 skipped
10 scenarios passed, 0 failed, 0 skipped
22 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.027s
.. note:: ``sismic-behave`` accepts multiple files for the ``--features`` parameter.
It also supports all the command-line parameters of Behave.
See `Behave command-line arguments <http://pythonhosted.org/behave/behave.html#command-line-arguments>`__ for more information.

Sismic is bundled with a set of predefined steps, allowing you to describe common statechart behavior without having to write a single line of Python code.

.. note:: If you do not want to rely on Sismic and want to use *behave* command-line interface, you can easily
import the predefined steps using ``from sismic.testing.steps import *``.
This will also import *behave* and all the needed objects to define and use new steps.
See `Python Step Implementations <http://pythonhosted.org/behave/tutorial.html#python-step-implementations>`__ for more information.


"Given" and "when" steps
------------------------

Given/when I do nothing
This step should be use to explicitly state that nothing is done.

Given/when I reproduce "{scenario}"
Reproduce the steps of given scenario's name.

.. literalinclude:: examples/elevator.feature
:language: gherkin
:lines: 7-10, 18-23
:emphasize-lines: 6

Given/when I repeat step "{step}" {repeat} times
Repeat given step.

Given I disable automatic execution
Some steps trigger an automatic execution of the statechart (like sending an event or
awaiting). With this step, one can turn of the automatic execution.

Given I enable automatic execution
Enable the automatic execution of the statechart (enabled by default).

Given/when I import a statechart from {path}
Import a statechart from a yaml file.
This step is implicitly executed when you use ``sismic-behave`` and is not needed unless
you call ``behave`` directly.

Given/when I execute the statechart
Execute the statechart, equivalent to :py:meth:`~sismic.interpreter.Interpreter.execute`.
Should not be used unless automatic execution is disabled.

Given/when I execute once the statechart
Execute the statechart, equivalent to :py:meth:`~sismic.interpreter.Interpreter.execute_once`.
Should not be used unless automatic execution is disabled.

Given/when I send event {event_name}
Send an event to the statechart, and execute the statechart if automatic execution is not disabled.

Given/when I send event {event_name} with {parameter}={value}
Send an event to the statechart with given parameter. The value of the parameter is evaluated as Python code.
The statechart is executed after this step.
Additional parameters can be specified using a table, as follows.

.. literalinclude:: examples/elevator.feature
:language: gherkin
:lines: 11-16
:emphasize-lines: 3-5

Given/when I wait {seconds} seconds
Increment the internal clock of the statechart, and perform a single execution of the statechart.
As the execution uses a simulated clock, if your statechart relies on relative time delta,
you should consider using the *repeated* version of this step.

Given/when I wait {seconds} seconds {repeat} times
Repeatedly increment the internal clock of the statechart, and execute the statechart after each increment.

.. literalinclude:: examples/microwave.feature
:language: gherkin
:lines: 14-18
:emphasize-lines: 3

Given I set variable {variable} to {value}
Set the value of a variable in the internal context of a statechart.
Value is evaluated as Python code.


"Then" steps
------------

Then state {state} should be active
Assert that given state name is active.

Then state {state} should not be active
Assert that given state name is not active.

Then event {event_name} should be fired
Assert that given event name is the latest fired event.

.. literalinclude:: examples/microwave.feature
:language: gherkin
:lines: 27-30
:emphasize-lines: 3

Then event {event_name} should be fired with {parameter}={value}
Assert that given event name was fired during the last execution, and that it has an attribute *parameter*
with given *value*, evaluated as Python code.
Additional parameters can be specified using a table.

Then no event should be fired
Assert that no event was fired by the statechart during the last execution.

Then variable {variable} should be defined
Assert that given *variable* is defined in the context of the statechart.

Then the value of variable {variable} should be {value}
Assert that given *variable* is defined and has given *value* in the context of the statechart.
The value is evaluated as Python code.

Then expression {expression} should hold
Assert that the truth value of given *expression* is true.
The expression is evaluated as Python code inside the context of the statechart.

Then the statechart is in a final configuration
Assert that the statechart is in a final configuration.


.. warning:: The documented list of steps, herebaove, could be incomplete or could contain slight variations.
You can easily get an up-to-date list of all the available steps using the ``--steps`` arguments,
as in ``sismic-behave elevator.yaml --features elevator.feature --steps``.
36 changes: 36 additions & 0 deletions docs/examples/elevator.feature
@@ -0,0 +1,36 @@
Feature: Elevator

Scenario: Elevator starts on ground floor
Then the value of current should be 0
And the value of destination should be 0

Scenario: Elevator can move to 7th floor
When I send event floorSelected with floor=7
Then the value of current should be 7

Scenario: Elevator can move to 4th floor
When I send event floorSelected
| parameter | value |
| floor | 4 |
| dummy | None |
Then the value of current should be 4

Scenario: Elevator reaches ground floor after 10 seconds
Given I reproduce "Elevator can move to 7th floor"
When I wait 10 seconds
Then the value of current should be 0
# Notice the variant using "holds":
And expression current == 0 should hold

Scenario Outline: Elevator can reach floor from 0 to 5
When I send event floorSelected with floor=<floor>
Then the value of current should be <floor>

Examples:
| floor |
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
30 changes: 30 additions & 0 deletions docs/examples/microwave.feature
@@ -0,0 +1,30 @@
Feature: microwave

Scenario: Microwave startup configuration
Then state heating.off should be active
And state lamp.off should be active
And state turntable.off should be active
And state door.close should be active

Scenario: Microwave starts
Given I send event incDuration
When I send event startstop
Then state heating.on should be active

Scenario: Microwave stops when the door is open
Given I reproduce "Microwave starts"
And I wait 1 second 3 times
When I send event toggledoor
Then state heating.on should not be active

Scenario: Microwave does not start if door is open
Given I send event toggledoor
And I send event incDuration
Then state door.open should be active
When I send event startstop
Then state heating.on should not be active

Scenario: Microwave bells when it stops heating
Given I reproduce "Microwave starts"
When I wait 1 second 5 times
Then event ding should be fired
1 change: 1 addition & 0 deletions docs/examples/microwave.yaml
Expand Up @@ -49,6 +49,7 @@ statechart:
guard: active('door.open')
- target: heating.off
guard: duration == 0
action: send('ding')
- target: heating.off
event: startstop
- guard: idle(1)
Expand Down
4 changes: 3 additions & 1 deletion docs/index.rst
Expand Up @@ -44,7 +44,8 @@ Sismic provides the following features:
- Synchronous and asynchronous simulation, in real time or simulated time
- Support for communication between statecharts and co-simulation
- Built-in support for expressing actions and guards using regular Python code
- A design-by-contract approach for statecharts: contracts can be specified to express invariants, pre- and postconditions on states, transitions and the statechart itself.
- A design-by-contract approach for statecharts: contracts can be specified to express invariants, pre- and postconditions on states and transitions.
- Predefined step definitions and utilities to support behavior-driven development.
- A unit testing framework for statecharts, including generation of test scenarios

Sismic statecharts provides full support for the majority of the UML 2 statechart concepts:
Expand Down Expand Up @@ -73,6 +74,7 @@ The Sismic library is written in a modular way:
code
stories
contract
behavior
testing


Expand Down
5 changes: 0 additions & 5 deletions requirements.txt

This file was deleted.

8 changes: 4 additions & 4 deletions setup.py
Expand Up @@ -73,7 +73,7 @@
# your project is installed. For an analysis of "install_requires" vs pip's
# requirements files see:
# https://packaging.python.org/en/latest/requirements.html
install_requires=['pyyaml>=3.11', 'pykwalify>=1.5.0'],
install_requires=['pyyaml>=3.11', 'pykwalify>=1.5.0', 'behave>=1.2.5'],

# List additional groups of dependencies here (e.g. development
# dependencies). You can install these using the following syntax,
Expand All @@ -92,8 +92,8 @@
# "scripts" keyword. Entry points provide cross-platform support and allow
# pip to create the appropriate form of executable for the target platform.
entry_points={
#'console_scripts': [
# 'sismic=sismic:_parse_args',
#],
'console_scripts': [
'sismic-behave=sismic.testing.behave:main',
],
},
)
2 changes: 1 addition & 1 deletion sismic/__init__.py
@@ -1,5 +1,5 @@
__description__ = 'Sismic Interactive State Machine Interpreter and Checker'
__version__ = '0.17.1'
__version__ = '0.18.0'
__url__ = 'https://github.com/AlexandreDecan/sismic/'
__author__ = 'Alexandre Decan'
__email__ = 'alexandre.decan@lexpage.net'
Expand Down
3 changes: 3 additions & 0 deletions sismic/testing/__init__.py
@@ -0,0 +1,3 @@
from .tester import teststory_from_trace

__all__ = ['teststory_from_trace']

0 comments on commit e7e0f4d

Please sign in to comment.