README.tutorial 5.28 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
Quick tutorial and overview
---------------------------

Lettuce is a framework for doing Behaviour Driven Development (BDD).

The idea behind BDD is that you first write down your requirements in
the form of scenarios, then implement their behaviour.

We do not plan on doing full BDD, but such a system should also help
us make system tests. And, hopefully, being able to better identify
what exactly is going wrong when a test fails.

Lettuce is a python implementation of the Cucumber framework, which is
a ruby system. So far we chose lettuce because we already need python
anyway, so chances are higher that any system we want to run it on
supports it. It only supports a subset of cucumber, but more cucumber
features are planned. As I do not know much details of cucumber, I
can't really say what is there and what is not.

A slight letdown is that the current version does not support python 3.
However, as long as the tool-calling glue is python2, this should not
cause any problems, since these aren't unit tests; We do not plan to use
our libraries directly, but only through the runnable scripts and
executables.

-----

Features, Scenarios, Steps.

Lettuce makes a distinction between features, scenarios, and steps.

Features are general, well, features. Each 'feature' has its own file
ending in .feature. A feature file contains a description and a number
of scenarios. Each scenario tests one or more particular parts of the
feature. Each scenario consists of a number of steps.

So let's open up a simple one.

-- example.feature
Feature: showing off BIND 10
    This is to show BIND 10 running and that it answer queries

    Scenario: Starting bind10
        # steps go here
--

I have predefined a number of steps we can use, as we build test we
will need to expand these, but we will look at them shortly.

This file defines a feature, just under the feature name we can
provide a description of the feature.

The one scenario we have no has no steps, so if we run it we should
see something like:

-- output
> lettuce
Feature: showing off BIND 10
  This is to show BIND 10 running and that it answer queries

  Scenario: Starting bind10

1 feature (1 passed)
1 scenario (1 passed)
0 step (0 passed)
--

Let's first add some steps that send queries.

--
        A query for www.example.com should have rcode REFUSED
        A query for www.example.org should have rcode NOERROR
--

Since we didn't start any bind10, dig will time out and the result
should be an error saying it got no answer. Errors are in the
form of stack traces (trigger by failed assertions), so we can find
out easily where in the tests they occurred. Especially when the total
set of steps gets bigger we might need that.

So let's add a step that starts bind10.

--
        When I start bind10 with configuration example.org.config
--

This is not good enough; it will fire of the process, but setting up
b10-auth may take a few moments, so we need to add a step to wait for
it to be started before we continue.

--
        Then wait for bind10 auth to start
--

And let's run the tests again.

--
> lettuce

Feature: showing off BIND 10
  This is to show BIND 10 running and that it answer queries

  Scenario: Starting bind10
    When I start bind10 with configuration example.org.config
    Then wait for bind10 auth to start
    A query for www.example.com should have rcode REFUSED
    A query for www.example.org should have rcode NOERROR

1 feature (1 passed)
1 scenario (1 passed)
4 steps (4 passed)
(finished within 2 seconds)
--

So take a look at one of those steps, let's pick the first one.

Jelte Jansen's avatar
Jelte Jansen committed
117
118
119
120
121
122
123
124
125
126
127
128
A step is defined through a python decorator, which in essence is a regular
expression; lettuce searches through all defined steps to find one that
matches. These are 'partial' matches (unless specified otherwise in the
regular expression itself), so if the step is defined with "do foo bar", the
scenario can add words for readability "When I do foo bar".

Each captured group will be passed as an argument to the function we define.
For bind10, i defined a configuration file, a cmdctl port, and a process
name. The first two should be self-evident, and the process name is an
optional name we give it, should we want to address it in the rest of the
tests. This is most useful if we want to start multiple instances. In the
next step (the wait for auth to start), I added a 'of <instance>'. So if we 
Jelte Jansen's avatar
Jelte Jansen committed
129
130
define the bind10 'as b10_second_instance', we can specify that one here as 
'of b10_second_instance'.
131

Jelte Jansen's avatar
Jelte Jansen committed
132
133
134
135
136
137
--
        When I start bind10 with configuration second.config
        with cmdctl port 12345 as b10_second_instance
--
(line wrapped for readability)

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
But notice how we needed two steps, which we probably always need (but
not entirely always)? We can also combine steps; for instance:

--
@step('have bind10 running(?: with configuration ([\w.]+))?')
def have_bind10_running(step, config_file):
    step.given('start bind10 with configuration ' + config_file)
    step.given('wait for bind10 auth to start')
--

Now we can replace the two steps with one:

--
    Given I have bind10 running
--

That's it for the quick overview. For some more examples, with comments, 
take a look at features/example.feature. You can read more about lettuce and
its features on http://www.lettuce.it, and if you plan on adding tests and
scenarios, please consult the last section of the main README first.