TestCenter Reference
BDD.py
Go to the documentation of this file.
1 #
2 # Copyright 2013, MeVis Medical Solutions AG
3 #
4 # The user may use this file in accordance with the license agreement provided with
5 # the Software or, alternatively, in accordance with the terms contained in a
6 # written agreement between the user and MeVis Medical Solutions AG.
7 #
8 # For further information use the contact form at https://www.mevislab.de/contact
9 #
10 
11 """Tools for writing human readable, behavioral tests.
12 
13 The functions, decorators and classes defined in this module enable
14 you to write tests describing the desired behavior of a MeVisLab
15 module (or any other Python object).
16 
17 A typical behavioral test looks like this:
18 
19 def TEST_a_behavioral_test():
20  Given().a_precondition(). \
21  When().you_do_something(). \
22  Then().something_happens()
23 
24 i.e. it is the concatenation of several function calls, or, in other words,
25 it is a sentence consisting of a Given clause, a When clause, and a Then
26 clause.
27 
28 Given(), When(), and Then() are predefined functions marking the start of
29 the Given clause (and of the whole sentence), the When clause, and the Then
30 clause, respectively.
31 
32 There is another predefined function, And(), which can be used to concatenate
33 several custom functions of the same type, e.g.
34 
35  Given().precondition1(). \
36  And().precondition2(). \
37  [...]
38 
39 The other functions are custom functions that are defined with the help
40 of three decorators: GivenClause, WhenClause, and ThenClause. (The decorators
41 add the decorated functions to the context class so that they can be called
42 as member functions.)
43 
44 The Given() function creates and returns a context object for the test. All
45 other functions are member functions of this context object. These functions
46 also return the context object. This allows arbitrary chaining of those
47 functions. By adding attributes to the context object we can pass arbitrary
48 state from one function to the next.
49 
50 See ExampleBDDTestCase in MeVisLab/Examples for an example.
51 
52 For more information on Behavior Driven Development (BDD) check the Internet.
53 
54 If you need a more powerful BDD-style language then have a look at one of the
55 existing Python BDD modules like behave, freshen, lettuce, etc.
56 """
57 from builtins import object
58 
59 import functools
60 
61 from TestSupport import Logging
62 
63 
64 def Given():
65  """Initiates a sentence describing a desired behavior.
66 
67  Creates and returns the context object for the test.
68  """
69  return _BDDContext()
70 
71 def GivenClause(func):
72  """Decorator for defining a Given clause.
73 
74  The first argument that is passed to the decorated function is the
75  context object. Custom parameters are passed to the decorated
76  function after the context object.
77 
78  The decorated function always returns the context object.
79 
80  Example:
81  @GivenClause
82  def a_precondition(testContext):
83  # perform the setup of the test, e.g. by parameterizing a TestPattern
84  # module
85  """
86  return _BDDClause('Given')(func)
87 
88 def WhenClause(func):
89  """Decorator for defining a When clause.
90 
91  The first argument that is passed to the decorated function is the
92  context of the test case. Custom parameters are passed to the decorated
93  function after the context.
94 
95  The decorated function always returns the context object.
96 
97  Example:
98  @WhenClause
99  def you_do_something(testContext):
100  # do whatever shall result in a certain behavior, e.g. touch a field
101  """
102  return _BDDClause('When')(func)
103 
104 def ThenClause(func):
105  """Decorator for defining a Then clause.
106 
107  The first argument that is passed to the decorated function is the
108  context of the test case. Custom parameters are passed to the decorated
109  function after the context.
110 
111  The decorated function always returns the context object.
112 
113  Example:
114  @ThenClause
115  def something_happens(testContext):
116  # check for the expected behavior, e.g. by comparing a field value
117  # with an expected value
118  """
119  return _BDDClause('Then')(func)
120 
121 
122 # Internal stuff
123 
124 class _BDDContext(object):
125  """Context of a BDD test case.
126 
127  Instances of this class are created with Given(). All functions defined
128  with the clause decorators are added to _BDDContext.
129  """
130 
131  def __init__(self):
132  self._state_state = 'Given'
133 
134  def And(self):
135  """Conjunction for joining two clauses of the same type.
136 
137  This method does nothing except returning the context. It is merely
138  syntactic sugar for joining two clauses of the same type, i.e. two
139  Given clauses, two When clauses, or two Then clauses.
140  """
141  return self
142 
143  def With(self):
144  """Conjunction for joining two clauses of the same type.
145 
146  This method does nothing except returning the context. It is merely
147  syntactic sugar for joining two clauses of the same type, i.e. two
148  Given clauses, two When clauses, or two Then clauses.
149  """
150  return self
151 
152  def When(self):
153  """Conjunction for marking the start of the When part.
154 
155  This method starts the When part of the sentence describing the
156  desired behavior. It returns the context.
157  """
158  self._state_state = 'When'
159  return self
160 
161  def Then(self):
162  """Conjunction for marking the start of the Then part.
163 
164  This method starts the Then part of the sentence describing the
165  desired behavior. It returns the context.
166  """
167  self._state_state = 'Then'
168  return self
169 
170  def __getattr__(self, name):
171  """Returns Given/When/Then method corresponding to @a name."""
172  prefix = self._state_state + '_'
173  if not name.startswith(prefix):
174  return getattr(self, prefix + name)
175  else:
176  raise AttributeError('Attribute with name "%s" not found.' % name)
177 
178 def _BDDClause(clauseType):
179  """Internal decorator used for defining clauses of different types."""
180  def func_wrapper(func):
181  @functools.wraps(func)
182  def wrapper(self, *args, **kwargs):
183  func(self, *args, **kwargs)
184  return self
185  setattr(_BDDContext, clauseType + '_' + func.__name__, wrapper)
186  return func_wrapper
def __getattr__(self, name)
Definition: BDD.py:170
def WhenClause(func)
Definition: BDD.py:88
def Given()
Definition: BDD.py:64
def ThenClause(func)
Definition: BDD.py:104
def GivenClause(func)
Definition: BDD.py:71