Name | pyspock JSON |
Version |
0.4.0
JSON |
| download |
home_page | None |
Summary | Python implementation for spock framework |
upload_time | 2025-07-21 08:35:04 |
maintainer | None |
docs_url | None |
author | ZhengYu, Xu |
requires_python | <4,>=3.9 |
license | None |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# spock
<div align="center">
<a href="https://github.com/astral-sh/ruff">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;">
</a>
<a href="https://github.com/astral-sh/uv">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv" style="max-width:100%;">
</a>
<a href="https://github.com/zen-xu/spock/actions">
<img src="https://github.com/zen-xu/spock/actions/workflows/test.yaml/badge.svg" alt="CI"/>
</a>
<a href="https://pypi.org/project/pyspock">
<img alt="PyPI" src="https://img.shields.io/pypi/v/pyspock">
</a>
<img src="https://img.shields.io/pypi/pyversions/pyspock">
<a href="https://github.com/zen-xu/spock/blob/main/LICENSE">
<img src="https://img.shields.io/badge/MIT%202.0-blue.svg" alt="License">
</a>
<a href="https://pepy.tech/project/pyspock">
<img src="https://pepy.tech/badge/pyspock/month"/>
</a>
</div>
`pyspock` is a BDD-style developer testing and specification framework for Python, and this is an implementation of the [`spock`](https://github.com/spockframework/spock).
An example of simple test:
```python
import pytest
@pytest.mark.spock("maximum of {a} and {b} is {c}")
def test_maximum():
def expect(a, b, c):
assert max(a, b) == c
def where(_, a, b, c):
_ | a | b | c
_ | 3 | 7 | 7
_ | 5 | 4 | 5
_ | 9 | 9 | 9
```
If you are using vscode, you can find there will be 3 test cases.

## Installation
```bash
pip install pyspock
```
Note the minimum version of pytest is 6.2.4.
## Mark
It is easy to define spock test cases, just use `pytest.mark.spock` to decorate your test function.
```python
@pytest.mark.spock
def test_bigger():
def expect():
assert 2 > 1
```
spock mark also accepts one parameter, which is used to render identity for each iteration arguments.
```python
@pytest.mark.spock("{a} > {b}")
def test_bigger():
def expect(a, b):
assert a > b
def where(_, a, b):
_ | a | b
_ | 7 | 3
_ | 5 | 2
```
## Blocks
There are six kinds of blocks: `given`, `when`, `then`, `expect`, `cleanup` and `where` blocks. Each block is a function defined by its name.
A test function must have at least one explicit block. Blocks divide a test function into distinct sections, and cannot be nested.

The above picture shows how blocks map to the conceptual phases of a feature function. The where block has a special role, which will be revealed shortly. But first, let’s have a closer look at the other blocks.
### 1. Given block
```python
@pytest.mark.spock
def test_demo():
def given(me):
me.stack = []
me.elem = 1
```
The `given` block is where you do any setup work for the feature that you are describing. It may not be preceded by other blocks. A `given` block doesn’t have any special semantic, and it is optional and may be omitted.
The `given` accepts all pytest fixtures, besides it has a special fixture named `me`, which can take any setup values. Those setup values can be as fixtures in other blocks except the `where` block.
### 2. When and Then blocks
```python
@pytest.mark.spock
def test_append_element():
def given(me):
me.stack = []
me.elem = 1
def when(stack, elem):
# stimulus
stack.append(elem)
def then(stack, elem):
# response
assert len(stack) == 1
assert stack.pop() == elem
```
The `when` and `then` blocks always occur together. They describe a stimulus and the expected response. Whereas when blocks may contain arbitrary code, then blocks are restricted to conditions, exception conditions, interactions.
The `then` has a special fixture named `excinfo`, which represents whether the `when` block raises exception. If the `when` block not raises exception, then `excinfo` will be None. The `excinfo` type is [ExceptionInfo](https://docs.pytest.org/en/stable/reference.html#exceptioninfo).
```python
@pytest.mark.spock
def test_zero_division():
def when():
1 / 0
def then(excinfo):
assert excinfo.type is ZeroDivisionError
```
### 3. Expect block
An `expect` block is more limited than a `then` block in that it may only contain conditions and variable definitions. It is useful in situations where it is more natural to describe stimulus and expected response in a single expression. For example, compare the following two attempts to describe the `max` function:
```python
@pytest.mark.spock
def test_maximum():
def given(me):
me.x = max(1, 2)
def then(x):
assert x == 2
```
equals to:
```python
@pytest.mark.spock
def test_maximum():
def expect():
assert max(1, 2) == 2
```
### 4. Cleanup block
```python
@pytest.mark.spock
def test_file():
def given(me):
me.file = open("test.txt", "r+")
def when(file):
file.write("hello")
def then(file):
file.seek(0)
assert file.read() == "hello"
def cleanup(file):
file.close()
```
`cleanup` block is used to free any resources used by a test case, and will run even if (a previous part of) the test case has produced an exception. As a consequence, a `cleanup` block must be coded defensively.
### 5. Where block
A `where` block always comes last in a test function. It is used to write data-driven feature functions. To give you an idea how this is done, have a look at the following example:
```python
@pytest.mark.spock("max({a}, {b}) == {c}")
def test_maximum_of_two_numbers():
def expect(a, b, c):
assert max(a, b) == c
def where(a, b, c):
a << [5, 3]
b << [1, 9]
c << [5, 9]
```
This `where` block effectively creates two "versions" of the feature functions: One where a is 5, b is 1, and c is 5, and another one where a is 3, b is 9, and c is 9.
Although it is declared last, the where block is evaluated before the feature function containing it runs.
#### Data table
`where` block support another syntax to specify the data.
```python
@pytest.mark.spock("max({a}, {b}) == {c}")
def test_maximum_of_two_numbers():
def expect(a, b, c):
assert max(a, b) == c
def where(_, a, b, c):
_ | a | b | c
_ | 5 | 1 | 5
_ | 3 | 9 | 9
```
The first line of the table, called the table header, declares the data variables. The subsequent lines, called table rows, hold the corresponding values. For each row, the feature method will get executed once; we call this an iteration of the function. If an iteration fails, the remaining iterations will nevertheless be executed. All failures will be reported.
You can also split the data table into multiple parts.
```python
@pytest.mark.spock("max({a}, {b}) == {c}")
def test_maximum_of_two_numbers():
def expect(a, b, c):
assert max(a, b) == c
def where(_, a, b, c):
_ | a | b
_ | 5 | 1
_ | 3 | 9
_ | c
_ | 5
_ | 9
```
#### Accessing other data variables
```python
def where(_, a, b):
_ | a | b
_ | 5 | a + 1
_ | 3 | a + 2
```
variable supports all operators like `+`, `-`, `*`, `/`, `%`, `**`, `//`, `==`, `!=`, `<`, `<=`, `>`, `>=` and so on.
#### Compatibility with black
if you are using `black`, you should close fmt when use data table.
```python
def where(_, a, b, c):
# fmt: off
_ | a | b | c
_ | 5 | a + 1 | 4
_ | 3 | a + c | 2
# fmt: on
```
### 6. Conclusion
| block | Support fixtures | Special fixtures | Optional |
| ----------- | ---------------- | ---------------- | -------------------------- |
| **given** | ✅ | `me` | ✅ |
| **when** | ✅ | ❎ | ✅ |
| **then** | ✅ | `excinfo` | when `expect` block exists |
| **expect** | ✅ | ❎ | when `then` block exists |
| **cleanup** | ✅ | ❎ | ✅ |
| **where** | ❎ | `_` | ✅ |
Raw data
{
"_id": null,
"home_page": null,
"name": "pyspock",
"maintainer": null,
"docs_url": null,
"requires_python": "<4,>=3.9",
"maintainer_email": null,
"keywords": null,
"author": "ZhengYu, Xu",
"author_email": "ZhengYu, Xu <zen-xu@outlook.com>",
"download_url": "https://files.pythonhosted.org/packages/15/64/e59258452a524d1162fecc5f0ff5b27e726fec424ad9a5fd9df218139293/pyspock-0.4.0.tar.gz",
"platform": null,
"description": "# spock\n\n<div align=\"center\">\n <a href=\"https://github.com/astral-sh/ruff\">\n <img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json\" alt=\"Ruff\" style=\"max-width:100%;\">\n </a>\n <a href=\"https://github.com/astral-sh/uv\">\n <img src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json\" alt=\"uv\" style=\"max-width:100%;\">\n </a>\n <a href=\"https://github.com/zen-xu/spock/actions\">\n <img src=\"https://github.com/zen-xu/spock/actions/workflows/test.yaml/badge.svg\" alt=\"CI\"/>\n </a>\n <a href=\"https://pypi.org/project/pyspock\">\n <img alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/pyspock\">\n </a>\n <img src=\"https://img.shields.io/pypi/pyversions/pyspock\">\n <a href=\"https://github.com/zen-xu/spock/blob/main/LICENSE\">\n <img src=\"https://img.shields.io/badge/MIT%202.0-blue.svg\" alt=\"License\">\n </a>\n <a href=\"https://pepy.tech/project/pyspock\">\n <img src=\"https://pepy.tech/badge/pyspock/month\"/>\n </a>\n</div>\n\n`pyspock` is a BDD-style developer testing and specification framework for Python, and this is an implementation of the [`spock`](https://github.com/spockframework/spock).\n\nAn example of simple test:\n\n```python\nimport pytest\n\n\n@pytest.mark.spock(\"maximum of {a} and {b} is {c}\")\ndef test_maximum():\n def expect(a, b, c):\n assert max(a, b) == c\n\n def where(_, a, b, c):\n _ | a | b | c\n _ | 3 | 7 | 7\n _ | 5 | 4 | 5\n _ | 9 | 9 | 9\n```\n\nIf you are using vscode, you can find there will be 3 test cases.\n\n\n\n## Installation\n\n```bash\npip install pyspock\n```\n\nNote the minimum version of pytest is 6.2.4.\n\n## Mark\n\nIt is easy to define spock test cases, just use `pytest.mark.spock` to decorate your test function.\n\n```python\n@pytest.mark.spock\ndef test_bigger():\n def expect():\n assert 2 > 1\n```\n\nspock mark also accepts one parameter, which is used to render identity for each iteration arguments.\n\n```python\n@pytest.mark.spock(\"{a} > {b}\")\ndef test_bigger():\n def expect(a, b):\n assert a > b\n\n def where(_, a, b):\n _ | a | b\n _ | 7 | 3\n _ | 5 | 2\n```\n\n## Blocks\n\nThere are six kinds of blocks: `given`, `when`, `then`, `expect`, `cleanup` and `where` blocks. Each block is a function defined by its name.\n\nA test function must have at least one explicit block. Blocks divide a test function into distinct sections, and cannot be nested.\n\n\n\nThe above picture shows how blocks map to the conceptual phases of a feature function. The where block has a special role, which will be revealed shortly. But first, let\u2019s have a closer look at the other blocks.\n\n\n### 1. Given block\n\n```python\n@pytest.mark.spock\ndef test_demo():\n def given(me):\n me.stack = []\n me.elem = 1\n```\n\nThe `given` block is where you do any setup work for the feature that you are describing. It may not be preceded by other blocks. A `given` block doesn\u2019t have any special semantic, and it is optional and may be omitted.\n\nThe `given` accepts all pytest fixtures, besides it has a special fixture named `me`, which can take any setup values. Those setup values can be as fixtures in other blocks except the `where` block.\n\n### 2. When and Then blocks\n\n```python\n@pytest.mark.spock\ndef test_append_element():\n def given(me):\n me.stack = []\n me.elem = 1\n\n def when(stack, elem):\n # stimulus\n stack.append(elem)\n\n def then(stack, elem):\n # response\n assert len(stack) == 1\n assert stack.pop() == elem\n```\n\nThe `when` and `then` blocks always occur together. They describe a stimulus and the expected response. Whereas when blocks may contain arbitrary code, then blocks are restricted to conditions, exception conditions, interactions.\n\nThe `then` has a special fixture named `excinfo`, which represents whether the `when` block raises exception. If the `when` block not raises exception, then `excinfo` will be None. The `excinfo` type is [ExceptionInfo](https://docs.pytest.org/en/stable/reference.html#exceptioninfo).\n\n\n```python\n@pytest.mark.spock\ndef test_zero_division():\n def when():\n 1 / 0\n\n def then(excinfo):\n assert excinfo.type is ZeroDivisionError\n```\n\n### 3. Expect block\n\nAn `expect` block is more limited than a `then` block in that it may only contain conditions and variable definitions. It is useful in situations where it is more natural to describe stimulus and expected response in a single expression. For example, compare the following two attempts to describe the `max` function:\n\n```python\n@pytest.mark.spock\ndef test_maximum():\n def given(me):\n me.x = max(1, 2)\n\n def then(x):\n assert x == 2\n```\n\nequals to:\n\n```python\n@pytest.mark.spock\ndef test_maximum():\n def expect():\n assert max(1, 2) == 2\n```\n\n### 4. Cleanup block\n\n```python\n@pytest.mark.spock\ndef test_file():\n def given(me):\n me.file = open(\"test.txt\", \"r+\")\n\n def when(file):\n file.write(\"hello\")\n\n def then(file):\n file.seek(0)\n assert file.read() == \"hello\"\n\n def cleanup(file):\n file.close()\n```\n\n `cleanup` block is used to free any resources used by a test case, and will run even if (a previous part of) the test case has produced an exception. As a consequence, a `cleanup` block must be coded defensively.\n\n### 5. Where block\n\nA `where` block always comes last in a test function. It is used to write data-driven feature functions. To give you an idea how this is done, have a look at the following example:\n\n```python\n@pytest.mark.spock(\"max({a}, {b}) == {c}\")\ndef test_maximum_of_two_numbers():\n def expect(a, b, c):\n assert max(a, b) == c\n\n def where(a, b, c):\n a << [5, 3]\n b << [1, 9]\n c << [5, 9]\n```\n\nThis `where` block effectively creates two \"versions\" of the feature functions: One where a is 5, b is 1, and c is 5, and another one where a is 3, b is 9, and c is 9.\n\nAlthough it is declared last, the where block is evaluated before the feature function containing it runs.\n\n#### Data table\n\n`where` block support another syntax to specify the data.\n\n```python\n@pytest.mark.spock(\"max({a}, {b}) == {c}\")\ndef test_maximum_of_two_numbers():\n def expect(a, b, c):\n assert max(a, b) == c\n\n def where(_, a, b, c):\n _ | a | b | c\n _ | 5 | 1 | 5\n _ | 3 | 9 | 9\n```\n\nThe first line of the table, called the table header, declares the data variables. The subsequent lines, called table rows, hold the corresponding values. For each row, the feature method will get executed once; we call this an iteration of the function. If an iteration fails, the remaining iterations will nevertheless be executed. All failures will be reported.\n\nYou can also split the data table into multiple parts.\n\n```python\n@pytest.mark.spock(\"max({a}, {b}) == {c}\")\ndef test_maximum_of_two_numbers():\n def expect(a, b, c):\n assert max(a, b) == c\n\n def where(_, a, b, c):\n _ | a | b\n _ | 5 | 1\n _ | 3 | 9\n\n _ | c\n _ | 5\n _ | 9\n```\n\n#### Accessing other data variables\n\n```python\ndef where(_, a, b):\n _ | a | b\n _ | 5 | a + 1\n _ | 3 | a + 2\n```\n\nvariable supports all operators like `+`, `-`, `*`, `/`, `%`, `**`, `//`, `==`, `!=`, `<`, `<=`, `>`, `>=` and so on.\n\n#### Compatibility with black\n\nif you are using `black`, you should close fmt when use data table.\n\n```python\ndef where(_, a, b, c):\n # fmt: off\n _ | a | b | c\n _ | 5 | a + 1 | 4\n _ | 3 | a + c | 2\n # fmt: on\n```\n\n### 6. Conclusion\n\n| block | Support fixtures | Special fixtures | Optional |\n| ----------- | ---------------- | ---------------- | -------------------------- |\n| **given** | \u2705 | `me` | \u2705 |\n| **when** | \u2705 | \u274e | \u2705 |\n| **then** | \u2705 | `excinfo` | when `expect` block exists |\n| **expect** | \u2705 | \u274e | when `then` block exists |\n| **cleanup** | \u2705 | \u274e | \u2705 |\n| **where** | \u274e | `_` | \u2705 |\n",
"bugtrack_url": null,
"license": null,
"summary": "Python implementation for spock framework",
"version": "0.4.0",
"project_urls": null,
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "140e3e11692e49971191665dabd0fa9ada5573aeba9491dbb5a603e93fac344a",
"md5": "ef33c132ec29fe0a7bd5c5fa207f1c77",
"sha256": "d85405a4dc5df6deaf4e84069f6ca7c34307a1cd8a00b1fdc843f7ae8d3761bd"
},
"downloads": -1,
"filename": "pyspock-0.4.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ef33c132ec29fe0a7bd5c5fa207f1c77",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4,>=3.9",
"size": 10810,
"upload_time": "2025-07-21T08:35:03",
"upload_time_iso_8601": "2025-07-21T08:35:03.009303Z",
"url": "https://files.pythonhosted.org/packages/14/0e/3e11692e49971191665dabd0fa9ada5573aeba9491dbb5a603e93fac344a/pyspock-0.4.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1564e59258452a524d1162fecc5f0ff5b27e726fec424ad9a5fd9df218139293",
"md5": "2f18fa47e16ca1c31ae2e078507efd56",
"sha256": "8d302fc2932da0aac2b09c38d1afe182b16178f1f47139ed861e8a7db19e50b3"
},
"downloads": -1,
"filename": "pyspock-0.4.0.tar.gz",
"has_sig": false,
"md5_digest": "2f18fa47e16ca1c31ae2e078507efd56",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4,>=3.9",
"size": 9023,
"upload_time": "2025-07-21T08:35:04",
"upload_time_iso_8601": "2025-07-21T08:35:04.165554Z",
"url": "https://files.pythonhosted.org/packages/15/64/e59258452a524d1162fecc5f0ff5b27e726fec424ad9a5fd9df218139293/pyspock-0.4.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-21 08:35:04",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "pyspock"
}