{ "info": { "author": "Daniel Farr\u00e9 Manzorro", "author_email": "d.farre.m@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6" ], "description": "# BDD Coder\nA package devoted to agile implementation of **class-based behavioural tests**. It consists of:\n\n* [coder](https://bitbucket.org/coleopter/bdd-coder/src/master/bdd_coder/coder) package able to\n\n - make a tester package - test suite - blueprint - see [example/tests](https://bitbucket.org/coleopter/bdd-coder/src/master/example/tests) - from user story specifications in YAML files - see [example/specs](https://bitbucket.org/coleopter/bdd-coder/src/master/example/specs),\n\n - patch such tester package with new YAML specifications - see [example/new_specs](https://bitbucket.org/coleopter/bdd-coder/src/master/example/new_specs) and [example/new_tests](https://bitbucket.org/coleopter/bdd-coder/src/master/example/new_tests)\n\n* [tester](https://bitbucket.org/coleopter/bdd-coder/src/master/bdd_coder/tester) package employed to run such blueprint tests, which also has the ability to export their docs as YAML specifications\n\nTest with [tox](https://tox.readthedocs.io/en/latest/) - see tox.ini.\n\nSee [mastermind](https://bitbucket.org/coleopter/mastermind) for an application.\n\n## Story\nThis package was born as a study of Behaviour Driven Development; and from the wish of having a handy implementation of Gherkin language in class-based tests, to be employed so that development cycles start with coding a behavioural test suite containing the scenario specifications in test case method `__doc__`s - as `bdd_coder.tester` achieves.\n\nIn conjunction with `bdd_coder.coder`, development cycles *start* with:\n\n1. A set of YAML specifications is agreed and crafted\n\n2. From these, a test suite is automatically created or patched\n\n3. New *test step methods* are crafted to efficiently achieve 100% behavioural coverage\n\n## User Story (feature) specifications\nEach test suite (tester package) has a structure\n```\n\u251c\u2500 __init__.py\n\u251c\u2500 aliases.py\n\u251c\u2500 base.py\n\u2514\u2500 test_stories.py\n```\ncorresponding to a specifications directory\n```\n\u251c\u2500 aliases.yml\n\u2514\u2500 features/\n \u251c\u2500 some-story.yml\n \u251c\u2500 another-story.yml\n \u251c\u2500 ...\n \u2514\u2500 this-story.yml\n```\nA story YAML file (the ones under features/) corresponds to a test case class declared into `test_stories.py`, consisting mainly of scenario declarations:\n```\nTitle: # --> class __name__\n\nStory: |- # free text --> class __doc__\n As a \n I want \n In order to/so that \n\nScenarios:\n Scenario name: # --> scenario __doc__\n - Step \"1\" with \"A\" gives `x` and `y`\n # ...\n - Last step with \"B\" gives `result`\n # ...\n\n# Extra class atributes - ignored in patching\nExtra name:\n \n...\n```\nSo only the keys `Title`, `Story`, `Scenarios` are reserved.\n\nScenario names are unique if `bdd_coder.tester.decorators.Steps` takes `validate=True` (the default), which also validates class hierarchy.\n\n### Step declarations\n* Start with a whole word - normally 'Given', 'When', or 'Then' - that is ignored by the tester (only order matters)\n\n* May contain:\n\n + Input `*args` sequence of values in double-quotes - passed to the step method\n\n + Output variable name sequence using backticks - if non-empty, the method should return the output values as a tuple, which are collected by the `bdd_coder.tester.decorators.Steps` decorator instance, by name into its `outputs` map of sequences\n\n* May refer to a scenario name, either belonging to the same class (story), or to an inherited class\n\n### Aliases\nDeclared as\n```\nAlias sentence: # --> method to call\n - Step sentence # from scenario __doc__s\n - Another step sentence\n # ...\n# ...\n```\ncorresponding to `aliases.py`:\n```\nMAP = {\n 'step_sentence': 'alias_sentence',\n 'another_step_sentence': 'alias_sentence',\n # ...\n}\n```\n\n## Tester\nThe core of each test suite consists of the following required class declarations in its `base.py` module:\n```\nfrom test.case.module import MyTestCase\n\nfrom bdd_coder.tester import decorators\nfrom bdd_coder.tester import tester\n\nfrom . import aliases\n\nsteps = decorators.Steps(aliases.MAP, logs_parent='example/tests')\n\n\n@steps\nclass BddTester(tester.BddTester):\n \"\"\"\n The decorated BddTester subclass of this suite - manages scenario runs\n \"\"\"\n\n\nclass BaseTestCase(tester.BaseTestCase, MyTestCase):\n \"\"\"\n The base test case of this suite - manages test runs\n \"\"\"\n```\nThen, story test cases are declared in `test_stories.py`, with\n```\nfrom . import base\nfrom bdd_coder.tester import decorators\n```\nas\n```\nclass StoryTitle(BddTesterSubclass, AnotherBddTesterSubclass, ...[, base.BaseTestCase]):\n```\nwith scenario declarations\n```\n @decorators.Scenario(base.steps):\n def [test_]scenario_name(self):\n \"\"\"\n Step \"1\" with \"A\" gives `x` and `y`\n ...\n Last step with \"B\" gives `result`\n \"\"\"\n```\nthat will run according to their `__doc__`s, and the necessary step method definitions.\n\n\n### Test run logging\nImplemented behavioural test step runs are logged by `bdd_coder.tester` as\n```\n1 \u2705 ClearBoard.even_boards:\n 1.1 - 2019-03-18 17:30:13.071420 \u2705 i_request_a_new_game_with_an_even_number_of_boards [] \u21a6 ('Even Game',)\n 1.2 - 2019-03-18 17:30:13.071420 \u2705 a_game_is_created_with_boards_of__guesses ['12'] \u21a6 ()\n\n2 \u2705 ClearBoard.test_start_board:\n 2.1 - 2019-03-18 17:30:13.071420 \u2705 even_boards [] \u21a6 ()\n 2.2 - 2019-03-18 17:30:13.071420 \u2705 i_request_a_clear_board_in_my_new_game [] \u21a6 ('Board',)\n 2.3 - 2019-03-18 17:30:13.071420 \u2705 board__is_added_to_the_game [] \u21a6 ()\n\n3 \u274c ClearBoard.test_odd_boards:\n 3.1 - 2019-03-18 17:30:13.071420 \u274c i_request_a_new_game_with_an_odd_number_of_boards [] \u21a6 Traceback (most recent call last):\n File \"/usr/lib/python3.6/unittest/mock.py\", line 939, in __call__\n return _mock_self._mock_call(*args, **kwargs)\n File \"/usr/lib/python3.6/unittest/mock.py\", line 995, in _mock_call\n raise effect\nAssertionError: FAKE\n\nScenario runs {\n \"1\u2705\": \"even_boards\",\n \"2\u2705\": \"test_start_board\"\n \"3\u274c\": \"test_odd_boards\"\n}\nPending []\n\nAll scenarios ran \u258c 2 \u2705 \u258c 1 \u274c\n```\ninto `$logs_parent/.bdd-run-logs/` (git-ignored), split by date into files `YYYY-MM-DD.log`, with the `logs_parent` value passed to `bdd_coder.tester.decorators.Steps`, which also has a `max_history_length` parameter - in days, older history is removed.\n\nIn Ubuntu I use the bash function\n```\nfunction bdd-log-tab() {\n gnome-terminal --tab -- tail -f $(pwd)/$1/.bdd-run-logs/$(ls $(pwd)/$1/.bdd-run-logs | tail -1)\n}\n```\nto open a terminal tab that will output the log stream as tests run (if the `.bdd-run-logs` directory exists).\n\n### Commands\n#### Check if pending scenarios\nIt may happen that all steps - and so all tests - that ran succeeded, but some scenarios were not reached. Run `bdd-pending-scenarios` after `pytest` to treat this as an error (recommended)\n```\n\u274c Some scenarios did not run! Check the logs in [...]/.bdd-run-logs\n```\n```\nusage: bdd-pending-scenarios [-h] logs_parent\n\npositional arguments:\n logs_parent Parent directory of .bdd-run-logs/\n```\n\n#### Export test suite docs as YAML\n```\nusage: bdd-make-yaml-specs [-h] [--overwrite] [--validate]\n test_module specs_path\n\npositional arguments:\n test_module passed to importlib.import_module\n specs_path will try to write the YAML files in here\n\noptional arguments:\n --overwrite, -w\n```\nAdditionally, validates code against generated specifications.\n\n## Coder commands\n### Make a test suite blueprint\n```\nusage: bdd-blueprint [-h] [--base-class BASE_CLASS]\n [--specs-path SPECS_PATH] [--tests-path TESTS_PATH]\n [--test-module-name TEST_MODULE_NAME] [--overwrite]\n\noptional arguments:\n --base-class BASE_CLASS, -c BASE_CLASS\n default: unittest.TestCase\n --specs-path SPECS_PATH, -i SPECS_PATH\n default: behaviour/specs\n --tests-path TESTS_PATH, -o TESTS_PATH\n default: next to specs\n --test-module-name TEST_MODULE_NAME, -n TEST_MODULE_NAME\n Name for test_.py. default: stories\n --overwrite\n```\nThe following:\n```\nbdd-coder$ bdd-blueprint -i example/specs -o example/tests --overwrite\n```\nwill rewrite [example/tests](https://bitbucket.org/coleopter/bdd-coder/src/master/example/tests) (with no changes if [example/specs](https://bitbucket.org/coleopter/bdd-coder/src/master/example/specs) is unmodified), and run `pytest` on the blueprint yielding the output, like\n```\n============================= test session starts ==============================\nplatform [...]\ncollecting ... collected 2 items\n\nexample/tests/test_stories.py::ClearBoard::test_odd_boards PASSED [ 50%]\nexample/tests/test_stories.py::ClearBoard::test_start_board PASSED [100%]\n\n=========================== 2 passed in 0.04 seconds ===========================\n```\n\n### Patch a test suite with new specifications\nUse this command in order to update a tester package with new YAML specifications - removes scenario declarations *only*, changes the scenario set, which may imply a new test class hierarchy with new stories and scenarios, adds the necessary step methods, and adds new aliases (if any).\n```\nusage: bdd-patch [-h] test_module [specs_path]\n\npositional arguments:\n test_module passed to importlib.import_module\n specs_path Directory to take new specs from. default: specs/ next to test package\n\noptional arguments:\n --scenario-delimiter SCENARIO_DELIMITER, -d SCENARIO_DELIMITER\n default: @decorators.Scenario(base.steps)\n```\nThe following:\n```\nbdd-coder$ bdd-patch example.tests.test_stories example/new_specs\n```\nwill turn [example/tests](https://bitbucket.org/coleopter/bdd-coder/src/master/example/tests) into [example/new_tests](https://bitbucket.org/coleopter/bdd-coder/src/master/example/new_tests), and run `pytest` on the suite yielding something like\n```\n============================= test session starts ==============================\nplatform [...]\ncollecting ... collected 3 items\n\nexample/tests/test_stories.py::NewGame::test_even_boards PASSED [ 33%]\nexample/tests/test_stories.py::NewGame::test_funny_boards PASSED [ 66%]\nexample/tests/test_stories.py::NewGame::test_more_boards PASSED [100%]\n\n=========================== 3 passed in 0.04 seconds ===========================\n```\nand a log\n```\n1 \u2705 NewGame.new_player_joins:\n 1.1 - 2019-04-01 00:30:50.164042 \u2705 a_user_signs_in [] \u21a6 ()\n 1.2 - 2019-04-01 00:30:50.164059 \u2705 a_new_player_is_added [] \u21a6 ()\n\n2 \u2705 NewGame.test_even_boards:\n 2.1 - 2019-04-01 00:30:50.164178 \u2705 new_player_joins [] \u21a6 ()\n 2.2 - 2019-04-01 00:30:50.164188 \u2705 i_request_a_new_game_with_an_even_number_of_boards [] \u21a6 ('game',)\n 2.3 - 2019-04-01 00:30:50.164193 \u2705 a_game_is_created_with_boards_of__guesses ['12'] \u21a6 ()\n\n3 \u2705 NewGame.new_player_joins:\n 3.1 - 2019-04-01 00:30:50.165339 \u2705 a_user_signs_in [] \u21a6 ()\n 3.2 - 2019-04-01 00:30:50.165348 \u2705 a_new_player_is_added [] \u21a6 ()\n\n4 \u2705 NewGame.test_funny_boards:\n 4.1 - 2019-04-01 00:30:50.165422 \u2705 new_player_joins [] \u21a6 ()\n 4.2 - 2019-04-01 00:30:50.165429 \u2705 class_hierarchy_has_changed [] \u21a6 ()\n\n5 \u2705 NewGame.new_player_joins:\n 5.1 - 2019-04-01 00:30:50.166458 \u2705 a_user_signs_in [] \u21a6 ()\n 5.2 - 2019-04-01 00:30:50.166466 \u2705 a_new_player_is_added [] \u21a6 ()\n\n6 \u2705 NewGame.test_more_boards:\n 6.1 - 2019-04-01 00:30:50.166535 \u2705 new_player_joins [] \u21a6 ()\n 6.2 - 2019-04-01 00:30:50.166541 \u2705 user_is_welcome [] \u21a6 ()\n\nScenario runs {\n \"1\u2705-3\u2705-5\u2705\": \"new_player_joins\",\n \"2\u2705\": \"test_even_boards\",\n \"4\u2705\": \"test_funny_boards\",\n \"6\u2705\": \"test_more_boards\"\n}\nPending []\n\nAll scenarios ran \u258c 6 \u2705\n```\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://bitbucket.org/coleopter/bdd-coder", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "bdd-coder", "package_url": "https://pypi.org/project/bdd-coder/", "platform": "", "project_url": "https://pypi.org/project/bdd-coder/", "project_urls": { "Homepage": "https://bitbucket.org/coleopter/bdd-coder" }, "release_url": "https://pypi.org/project/bdd-coder/1.1.0rc1/", "requires_dist": [ "pyyaml", "argparse", "pytest", "flake8", "simple-cmd", "ipdb ; extra == 'dev'", "ipython ; extra == 'dev'", "pytest-cov ; extra == 'test'", "freezegun ; extra == 'test'" ], "requires_python": "", "summary": "Gherkin language in class-based tests - test suite blueprinting", "version": "1.1.0rc1" }, "last_serial": 5121582, "releases": { "1.1.0rc0": [ { "comment_text": "", "digests": { "md5": "76e60324140d69182dd9d50ad3b54a76", "sha256": "7d29ddaaafcd0e63e018a1d41a1aebb54f7d4ec1a96a56dd1cb4de3d9bf005da" }, "downloads": -1, "filename": "bdd_coder-1.1.0rc0-py3-none-any.whl", "has_sig": false, "md5_digest": "76e60324140d69182dd9d50ad3b54a76", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26289, "upload_time": "2019-04-09T00:09:26", "url": "https://files.pythonhosted.org/packages/50/e9/af496cef911486757268920030dcc31c9fb72e3a28efcf0aa0b134768611/bdd_coder-1.1.0rc0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "57c9c410f92bfdd18c3ca1a204ff6058", "sha256": "46d109ce58795997683465fd76d47dc3090f2f86580d68523d05de02e50bd2a1" }, "downloads": -1, "filename": "bdd-coder-1.1.0rc0.tar.gz", "has_sig": false, "md5_digest": "57c9c410f92bfdd18c3ca1a204ff6058", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22860, "upload_time": "2019-04-09T00:09:29", "url": "https://files.pythonhosted.org/packages/da/d1/5403ff19c10ee2d51cbe8493b68e41b02ee3d66060e8195d7acca9277778/bdd-coder-1.1.0rc0.tar.gz" } ], "1.1.0rc1": [ { "comment_text": "", "digests": { "md5": "b66695196f23173d04821506dd75c6a4", "sha256": "cf70ed68e8115bac3e3328792a519950b05f3f7ee9295a60460882bccec28c75" }, "downloads": -1, "filename": "bdd_coder-1.1.0rc1-py3-none-any.whl", "has_sig": false, "md5_digest": "b66695196f23173d04821506dd75c6a4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24762, "upload_time": "2019-04-10T01:07:30", "url": "https://files.pythonhosted.org/packages/06/7e/e13741e6baeba888427d1b134b0af9349d18fc19e1d63d7438281fa31563/bdd_coder-1.1.0rc1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ca2b5f18512dad77c828f0d3688eb923", "sha256": "ba1b10fa270cee3504c532877d8de4637398da26ccf881fff3eac3e12ed1db9d" }, "downloads": -1, "filename": "bdd-coder-1.1.0rc1.tar.gz", "has_sig": false, "md5_digest": "ca2b5f18512dad77c828f0d3688eb923", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21593, "upload_time": "2019-04-10T01:07:32", "url": "https://files.pythonhosted.org/packages/8d/ae/3b07e618ac16268a25db1096507f90f58b2d30aa0e4e4b635b7c831ff0e4/bdd-coder-1.1.0rc1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b66695196f23173d04821506dd75c6a4", "sha256": "cf70ed68e8115bac3e3328792a519950b05f3f7ee9295a60460882bccec28c75" }, "downloads": -1, "filename": "bdd_coder-1.1.0rc1-py3-none-any.whl", "has_sig": false, "md5_digest": "b66695196f23173d04821506dd75c6a4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 24762, "upload_time": "2019-04-10T01:07:30", "url": "https://files.pythonhosted.org/packages/06/7e/e13741e6baeba888427d1b134b0af9349d18fc19e1d63d7438281fa31563/bdd_coder-1.1.0rc1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ca2b5f18512dad77c828f0d3688eb923", "sha256": "ba1b10fa270cee3504c532877d8de4637398da26ccf881fff3eac3e12ed1db9d" }, "downloads": -1, "filename": "bdd-coder-1.1.0rc1.tar.gz", "has_sig": false, "md5_digest": "ca2b5f18512dad77c828f0d3688eb923", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21593, "upload_time": "2019-04-10T01:07:32", "url": "https://files.pythonhosted.org/packages/8d/ae/3b07e618ac16268a25db1096507f90f58b2d30aa0e4e4b635b7c831ff0e4/bdd-coder-1.1.0rc1.tar.gz" } ] }