类测试中的小黄瓜语言.测试套件蓝图
bdd-coder的Python项目详细描述
bdd编码器
一个致力于基于类的行为测试的敏捷实现的包。它包括:
coder包能够
制作一个测试包-测试套件-蓝图-参见example/tests-来自yaml文件中的用户故事规范-参见example/specs,
用新的yaml规范修补这样的测试包-请参见example/new_specs和example/new_tests
tester用于运行此类蓝图测试的包,该包还能够将其文档导出为yaml规范
用tox测试-见tox.ini。
有关应用程序,请参见mastermind。
故事
这个包是作为行为驱动开发的一个研究而诞生的;并且是为了在基于类的测试中方便地实现gherkin语言,以便开发周期从编写包含测试用例方法__doc__
s-as bdd_coder.tester
中的场景规范的行为测试套件开始的。
结合bdd_coder.coder
,开发周期start使用:
已商定并制作了一套山药规范
从中自动创建或修补测试套件
新的测试步骤方法经过精心设计,可有效实现100%的行为覆盖率
用户情景(功能)规范
每个测试套件(测试仪包)都有一个结构
├─ __init__.py
├─ aliases.py
├─ base.py
└─ test_stories.py
对应于规范目录
├─ aliases.yml
└─ features/
├─ some-story.yml
├─ another-story.yml
├─ ...
└─ this-story.yml
story yaml文件(features/下的文件)对应于声明为test_stories.py
的测试用例类,主要由场景声明组成:
Title: <Story title> # --> class __name__
Story: |- # free text --> class __doc__
As a <user group>
I want <feature>
In order to/so that <goal>
Scenarios:
Scenario name: # --> scenario __doc__
- Step "1" with "A" gives `x` and `y`
# ...
- Last step with "B" gives `result`
# ...
# Extra class atributes - ignored in patching
Extra name:
<yaml-attribute-coded-with-str(yaml.load)>
...
所以只有键Title
、Story
、Scenarios
被保留。
如果bdd_coder.tester.decorators.Steps
采用validate=True
(默认值),则方案名称是唯一的,这也验证了类层次结构。
步骤声明
从一个完整的单词开始-通常是“给定”、“何时”或“然后”-测试人员会忽略这个单词(仅限订单)
可能包含:
输入
*args
双引号中的值序列-传递给step方法使用反勾号输出变量名称序列-如果非空,则方法应将输出值作为元组返回,这些值由
bdd_coder.tester.decorators.Steps
装饰器实例收集,并按名称返回到其序列的outputs
映射中
可能是指场景名称,属于同一个类(story)或继承的类
别名
声明为
Alias sentence: # --> method to call
- Step sentence # from scenario __doc__s
- Another step sentence
# ...
# ...
对应于aliases.py
:
MAP = {
'step_sentence': 'alias_sentence',
'another_step_sentence': 'alias_sentence',
# ...
}
测试仪
每个测试套件的核心由其base.py
模块中的以下必需类声明组成:
from test.case.module import MyTestCase
from bdd_coder.tester import decorators
from bdd_coder.tester import tester
from . import aliases
steps = decorators.Steps(aliases.MAP, logs_parent='example/tests')
@steps
class BddTester(tester.BddTester):
"""
The decorated BddTester subclass of this suite - manages scenario runs
"""
class BaseTestCase(tester.BaseTestCase, MyTestCase):
"""
The base test case of this suite - manages test runs
"""
然后,在test_stories.py
中声明故事测试用例,并使用
from . import base
from bdd_coder.tester import decorators
作为
class StoryTitle(BddTesterSubclass, AnotherBddTesterSubclass, ...[, base.BaseTestCase]):
使用场景声明
@decorators.Scenario(base.steps):
def [test_]scenario_name(self):
"""
Step "1" with "A" gives `x` and `y`
...
Last step with "B" gives `result`
"""
它将根据它们的__doc__
和必要的步骤方法定义运行。
测试运行日志
已实现的行为测试步骤运行由bdd_coder.tester
记录为
1 ✅ ClearBoard.even_boards:
1.1 - 2019-03-18 17:30:13.071420 ✅ i_request_a_new_game_with_an_even_number_of_boards [] ↦ ('Even Game',)
1.2 - 2019-03-18 17:30:13.071420 ✅ a_game_is_created_with_boards_of__guesses ['12'] ↦ ()
2 ✅ ClearBoard.test_start_board:
2.1 - 2019-03-18 17:30:13.071420 ✅ even_boards [] ↦ ()
2.2 - 2019-03-18 17:30:13.071420 ✅ i_request_a_clear_board_in_my_new_game [] ↦ ('Board',)
2.3 - 2019-03-18 17:30:13.071420 ✅ board__is_added_to_the_game [] ↦ ()
3 ❌ ClearBoard.test_odd_boards:
3.1 - 2019-03-18 17:30:13.071420 ❌ i_request_a_new_game_with_an_odd_number_of_boards [] ↦ Traceback (most recent call last):
File "/usr/lib/python3.6/unittest/mock.py", line 939, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/usr/lib/python3.6/unittest/mock.py", line 995, in _mock_call
raise effect
AssertionError: FAKE
Scenario runs {
"1✅": "even_boards",
"2✅": "test_start_board"
"3❌": "test_odd_boards"
}
Pending []
All scenarios ran ▌ 2 ✅ ▌ 1 ❌
in to $logs_parent/.bdd-run-logs/
(git忽略),按日期拆分成文件YYYY-MM-DD.log
,并将logs_parent
值传递给bdd_coder.tester.decorators.Steps
,后者也有一个max_history_length
参数-在数天内,旧的历史记录将被删除。
在ubuntu中,我使用bash函数
function bdd-log-tab() {
gnome-terminal --tab -- tail -f $(pwd)/$1/.bdd-run-logs/$(ls $(pwd)/$1/.bdd-run-logs | tail -1)
}
打开一个终端标签,当测试运行时将输出日志流(如果存在{{
命令
检查是否有挂起的方案
可能发生的情况是,运行的所有步骤(以及所有测试)都成功了,但有些场景没有达到。在pytest
之后运行bdd-pending-scenarios
,将其视为错误(推荐)
❌ Some scenarios did not run! Check the logs in [...]/.bdd-run-logs
usage: bdd-pending-scenarios [-h] logs_parent
positional arguments:
logs_parent Parent directory of .bdd-run-logs/
将测试套件文档导出为yaml
usage: bdd-make-yaml-specs [-h] [--overwrite] [--validate]
test_module specs_path
positional arguments:
test_module passed to importlib.import_module
specs_path will try to write the YAML files in here
optional arguments:
--overwrite, -w
usage: bdd-make-yaml-specs [-h] [--overwrite] [--validate]
test_module specs_path
positional arguments:
test_module passed to importlib.import_module
specs_path will try to write the YAML files in here
optional arguments:
--overwrite, -w
另外,根据生成的规范验证代码。
编码器命令
制作测试套件蓝图
usage: bdd-blueprint [-h] [--base-class BASE_CLASS]
[--specs-path SPECS_PATH] [--tests-path TESTS_PATH]
[--test-module-name TEST_MODULE_NAME] [--overwrite]
optional arguments:
--base-class BASE_CLASS, -c BASE_CLASS
default: unittest.TestCase
--specs-path SPECS_PATH, -i SPECS_PATH
default: behaviour/specs
--tests-path TESTS_PATH, -o TESTS_PATH
default: next to specs
--test-module-name TEST_MODULE_NAME, -n TEST_MODULE_NAME
Name for test_<name>.py. default: stories
--overwrite
T型他跟着:
bdd-coder$ bdd-blueprint -i example/specs -o example/tests --overwrite
将重写example/tests(如果example/specs未修改,则不做任何更改),并在生成输出的蓝图上运行pytest
,如
============================= test session starts ==============================
platform [...]
collecting ... collected 2 items
example/tests/test_stories.py::ClearBoard::test_odd_boards PASSED [ 50%]
example/tests/test_stories.py::ClearBoard::test_start_board PASSED [100%]
=========================== 2 passed in 0.04 seconds ===========================
使用新规范修补测试套件
使用此命令可使用新的yaml规范更新测试程序包-仅删除场景声明only,更改场景集,这可能意味着新的测试类层次结构包含新的场景和场景,添加必要的步骤方法,并添加新的别名(如果有)。
usage: bdd-patch [-h] test_module [specs_path]
positional arguments:
test_module passed to importlib.import_module
specs_path Directory to take new specs from. default: specs/ next to test package
optional arguments:
--scenario-delimiter SCENARIO_DELIMITER, -d SCENARIO_DELIMITER
default: @decorators.Scenario(base.steps)
以下内容:
bdd-coder$ bdd-patch example.tests.test_stories example/new_specs
将example/tests转换为example/new_tests,并在套件上运行pytest
,生成类似
============================= test session starts ==============================
platform [...]
collecting ... collected 3 items
example/tests/test_stories.py::NewGame::test_even_boards PASSED [ 33%]
example/tests/test_stories.py::NewGame::test_funny_boards PASSED [ 66%]
example/tests/test_stories.py::NewGame::test_more_boards PASSED [100%]
=========================== 3 passed in 0.04 seconds ===========================
和一个日志
1 ✅ NewGame.new_player_joins:
1.1 - 2019-04-01 00:30:50.164042 ✅ a_user_signs_in [] ↦ ()
1.2 - 2019-04-01 00:30:50.164059 ✅ a_new_player_is_added [] ↦ ()
2 ✅ NewGame.test_even_boards:
2.1 - 2019-04-01 00:30:50.164178 ✅ new_player_joins [] ↦ ()
2.2 - 2019-04-01 00:30:50.164188 ✅ i_request_a_new_game_with_an_even_number_of_boards [] ↦ ('game',)
2.3 - 2019-04-01 00:30:50.164193 ✅ a_game_is_created_with_boards_of__guesses ['12'] ↦ ()
3 ✅ NewGame.new_player_joins:
3.1 - 2019-04-01 00:30:50.165339 ✅ a_user_signs_in [] ↦ ()
3.2 - 2019-04-01 00:30:50.165348 ✅ a_new_player_is_added [] ↦ ()
4 ✅ NewGame.test_funny_boards:
4.1 - 2019-04-01 00:30:50.165422 ✅ new_player_joins [] ↦ ()
4.2 - 2019-04-01 00:30:50.165429 ✅ class_hierarchy_has_changed [] ↦ ()
5 ✅ NewGame.new_player_joins:
5.1 - 2019-04-01 00:30:50.166458 ✅ a_user_signs_in [] ↦ ()
5.2 - 2019-04-01 00:30:50.166466 ✅ a_new_player_is_added [] ↦ ()
6 ✅ NewGame.test_more_boards:
6.1 - 2019-04-01 00:30:50.166535 ✅ new_player_joins [] ↦ ()
6.2 - 2019-04-01 00:30:50.166541 ✅ user_is_welcome [] ↦ ()
Scenario runs {
"1✅-3✅-5✅": "new_player_joins",
"2✅": "test_even_boards",
"4✅": "test_funny_boards",
"6✅": "test_more_boards"
}
Pending []
All scenarios ran ▌ 6 ✅