@@ -8,7 +8,51 @@ that allows tests to be written in arbitrary nested describe-blocks,
88similar to RSpec (Ruby) and Jasmine (JavaScript).
99
1010The main inspiration for this was
11- a [ video] ( https://www.youtube.com/watch?v=JJle8L8FRy0> ) by Gary Bernhardt.
11+ a [ video] ( https://www.youtube.com/watch?v=JJle8L8FRy0 ) by Gary Bernhardt.
12+
13+ ## Why bother?
14+
15+ I've found that quite often my tests have one "dimension" more than my production
16+ code. The production code is organized into packages, modules, classes
17+ (sometimes), and functions. I like to organize my tests in the same way, but
18+ tests also have different * cases* for each function. This tends to end up with
19+ a set of tests for each module (or class), where each test has to name both a
20+ function and a * case* . For instance:
21+
22+ ``` python
23+ def test_my_function_with_default_arguments ():
24+ def test_my_function_with_some_other_arguments ():
25+ def test_my_function_throws_exception ():
26+ def test_my_function_handles_exception ():
27+ def test_some_other_function_returns_true ():
28+ def test_some_other_function_returns_false ():
29+ ```
30+
31+ It's much nicer to do this:
32+
33+ ``` python
34+ def describe_my_function ():
35+ def with_default_arguments ():
36+ def with_some_other_arguments ():
37+ def it_throws_exception ():
38+ def it_handles_exception ():
39+
40+ def describe_some_other_function ():
41+ def it_returns_true ():
42+ def it_returns_false ():
43+ ```
44+
45+ It has the additional advantage that you can have marks and fixtures that apply
46+ locally to each group of test functions.
47+
48+ With pytest, it's possible to organize tests in a similar way with classes.
49+ However, I think classes are awkward. I don't think the convention of using
50+ camel-case names for classes fit very well when testing functions in different
51+ cases. In addition, every test function must take a "self" argument that is
52+ never used.
53+
54+ The pytest-describe plugin allows organizing your tests in the nicer way shown
55+ above using describe-blocks.
1256
1357## Installation
1458
@@ -112,8 +156,10 @@ describe_prefixes = ["custom_prefix_"]
112156```
113157
114158Functions prefixed with ` _ ` in the describe-block are not collected as tests.
115- This can be used to group helper functions. Otherwise, functions inside the
116- describe-blocks need not follow any special naming convention.
159+ This can be used to group helper functions. Thanks to closures, a helper
160+ defined in an enclosing describe-block is visible in all nested blocks.
161+ Otherwise, functions inside the describe-blocks need not follow any special
162+ naming convention.
117163
118164``` python
119165def describe_function ():
@@ -126,7 +172,6 @@ def describe_function():
126172 ...
127173```
128174
129-
130175## Fixtures as describe arguments
131176
132177When several tests in a describe-block need the same fixture, you can pass
@@ -145,10 +190,10 @@ def user():
145190def describe_create_book (user ):
146191
147192 def with_valid_book (valid_book ):
148- # use the user and valid_book fixtures ...
193+ ... # use the user and valid_book fixtures
149194
150195 def with_invalid_book (invalid_book ):
151- # use the user and invalid_book fixtures ...
196+ ... # use the user and invalid_book fixtures
152197```
153198
154199This is functionally equivalent to declaring the fixture as an argument of
@@ -175,6 +220,42 @@ For the same reason, fixtures with a scope higher than `function` and
175220autouse fixtures should not use describe arguments, because they may be
176221set up before the values are injected.
177222
223+ ## Shared behaviors
224+
225+ If you've used RSpec's shared examples or test class inheritance, then you may
226+ be familiar with the benefit of having the same tests apply to
227+ multiple "subjects" or "SUTs" (systems under test).
228+
229+ ``` python
230+ from pytest import fixture
231+ from pytest_describe import behaves_like
232+
233+ def a_duck ():
234+ def it_quacks (sound ):
235+ assert sound == " quack"
236+
237+ @behaves_like (a_duck)
238+ def describe_something_that_quacks ():
239+ @fixture
240+ def sound ():
241+ return " quack"
242+
243+ # the it_quacks test in this describe will pass
244+
245+ @behaves_like (a_duck)
246+ def describe_something_that_barks ():
247+ @fixture
248+ def sound ():
249+ return " bark"
250+
251+ # the it_quacks test in this describe will fail (as expected)
252+ ```
253+
254+ Fixtures defined in the block that includes the shared behavior take precedence
255+ over fixtures defined in the shared behavior. This rule only applies to
256+ fixtures, not to other functions (nested describe blocks and tests). Instead,
257+ they are all collected as separate tests.
258+
178259## Using docstrings as describe block names
179260
180261By default, describe-blocks are reported under the name of their function,
@@ -210,86 +291,6 @@ test_wallet.py::a wallet::when it is empty::it_has_no_balance PASSED
210291Note that the docstring-based names become part of the test node IDs, which
211292are used when selecting tests with ` -k ` or by node ID on the command line.
212293
213- ## Why bother?
214-
215- I've found that quite often my tests have one "dimension" more than my production
216- code. The production code is organized into packages, modules, classes
217- (sometimes), and functions. I like to organize my tests in the same way, but
218- tests also have different * cases* for each function. This tends to end up with
219- a set of tests for each module (or class), where each test has to name both a
220- function and a * case* . For instance:
221-
222- ``` python
223- def test_my_function_with_default_arguments ():
224- def test_my_function_with_some_other_arguments ():
225- def test_my_function_throws_exception ():
226- def test_my_function_handles_exception ():
227- def test_some_other_function_returns_true ():
228- def test_some_other_function_returns_false ():
229- ```
230-
231- It's much nicer to do this:
232-
233- ``` python
234- def describe_my_function ():
235- def with_default_arguments ():
236- def with_some_other_arguments ():
237- def it_throws_exception ():
238- def it_handles_exception ():
239-
240- def describe_some_other_function ():
241- def it_returns_true ():
242- def it_returns_false ():
243- ```
244-
245- It has the additional advantage that you can have marks and fixtures that apply
246- locally to each group of test function.
247-
248- With pytest, it's possible to organize tests in a similar way with classes.
249- However, I think classes are awkward. I don't think the convention of using
250- camel-case names for classes fit very well when testing functions in different
251- cases. In addition, every test function must take a "self" argument that is
252- never used.
253-
254- The pytest-describe plugin allows organizing your tests in the nicer way shown
255- above using describe-blocks.
256-
257- ## Shared Behaviors
258-
259- If you've used rspec's shared examples or test class inheritance, then you may
260- be familiar with the benefit of having the same tests apply to
261- multiple "subjects" or "suts" (system under test).
262-
263- ``` python
264- from pytest import fixture
265- from pytest_describe import behaves_like
266-
267- def a_duck ():
268- def it_quacks (sound ):
269- assert sound == " quack"
270-
271- @behaves_like (a_duck)
272- def describe_something_that_quacks ():
273- @fixture
274- def sound ():
275- return " quack"
276-
277- # the it_quacks test in this describe will pass
278-
279- @behaves_like (a_duck)
280- def describe_something_that_barks ():
281- @fixture
282- def sound ():
283- return " bark"
284-
285- # the it_quacks test in this describe will fail (as expected)
286- ```
287-
288- Fixtures defined in the block that includes the shared behavior take precedence
289- over fixtures defined in the shared behavior. This rule only applies to
290- fixtures, not to other functions (nested describe blocks and tests). Instead,
291- they are all collected as separate tests.
292-
293294## Accessing describe functions from plugins
294295
295296Reporting plugins sometimes need to know which describe-blocks enclose a
0 commit comments