Name | ops-scenario JSON |
Version |
8.2.0
JSON |
| download |
home_page | None |
Summary | Python library providing a state-transition testing API for Operator Framework charms. |
upload_time | 2025-08-27 22:58:39 |
maintainer | The Charm Tech team at Canonical Ltd. |
docs_url | None |
author | None |
requires_python | >=3.10 |
license | None |
keywords |
juju
test
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# ops-scenario, the unit testing framework for ops charms
`ops-scenario` is a Python library that provides state-transition testing for
[Ops](https://documentation.ubuntu.com/ops/latest/) charms. These tests are higher level than
typical unit tests, but run at similar speeds and are the recommended approach
for testing charms within requiring a full [Juju](https://juju.is) installation.
Test are written in the arrange/act/assert pattern, arranging an object
representing the current Juju state, acting by emulating an event from Juju, and
then asserting on the (simulated) output Juju state.
## Writing tests
Here's a test that verifies that a unit is active after the `start` event, with a very minimal initial state:
```python
from ops import testing
# 'src/charm.py' typically contains the charm class.
from charm import MyCharm
def test_start():
ctx = testing.Context(MyCharm)
state_in = testing.State()
state_out = ctx.run(ctx.on.start(), state_in)
assert state_out.unit_status == testing.ActiveStatus()
```
More comprehensive tests will include relations, containers, secrets, and other
components in the input state, and assertions against both the output state and
the context. The 'act' stage remains a simple single call, although additional
arguments may be required for the event, such as the relation or container that
triggered it. For example:
```python
import pytest
from ops import testing
from charm import MyCharm
@pytest.mark.parametrize(
'leader',
[pytest.param(True, id='leader'), pytest.param(False, id='non-leader')],
)
def test_(leader: bool):
# Arrange:
ctx = testing.Context(MyCharm)
relation = testing.Relation('db', local_app_data={'hostname': 'example.com'})
peer_relation = testing.PeerRelation('peer')
container = testing.Container('workload', can_connect=True)
relation_secret = testing.Secret({'certificate': 'xxxxxxxx'})
user_secret = testing.Secret({'username': 'admin', 'password': 'xxxxxxxx'})
config = {'port': 8443, 'admin-credentials': 'secret:1234'}
state_in = testing.State(
leader=leader,
config=config,
relations={relation, peer_relation},
containers={container},
secrets={relation_secret, user_secret},
unit_status=testing.BlockedStatus(),
workload_version='1.0.1',
)
# Act:
state_out = ctx.run(ctx.on.relation_changed(relation), state_in)
# Assert:
assert testing.JujuLogLine(level='INFO', message='Distributing secret.') in ctx.juju_log
peer_relation_out = state_out.get_relation(peer_relation.id)
assert peer_relation_out.peers_data[0] == {'secret_id': relation_secret.id}
```
You don't have to use pytest for your charm tests, but it's what we recommend.
pytest's `assert`-based approach is a straightforward way to write tests, and
its fixtures are helpful for structuring setup and teardown.
## Installation
For charm tests, install the testing framework by adding the `testing` extra of
ops in your unit testing environment. For example, in `pyproject.toml`:
```toml
[dependency-groups]
test = ['ops[testing]<4.0']
```
Ops checks if `ops-scenario` is installed, and, if so, makes the classes
(such as `Context`, `State`, and `Relation`) available in the `ops.testing`
namespace. Use `from ops import testing` rather than importing the `scenario`
package.
`ops-scenario` supports the same platforms and Python versions as ops itself.
## Documentation
* To get started, work through our ['Write your first Kubernetes charm' tutorial](https://documentation.ubuntu.com/ops/latest/tutorial/from-zero-to-hero-write-your-first-kubernetes-charm/create-a-minimal-kubernetes-charm/#write-unit-tests-for-your-charm), following the instructions for adding
unit tests at the end of each chapter.
* When you need to write a test that involves specific ops functionality,
refer to our [how-to guides](https://documentation.ubuntu.com/ops/latest/howto/)
which all conclude with examples of tests of the ops functionality.
* Use our extensive [reference documentation](https://documentation.ubuntu.com/ops/latest/reference/ops-testing/#ops-testing) when you need to know how each `testing` object works. These
docs are also available via the standard Python `help()` functionality and in
your IDE.
[**Read the full documentation**](https://documentation.ubuntu.com/ops/latest/)
## Community
`ops-scenario` is a member of the Charming family. It's an open source project
that warmly welcomes community contributions, suggestions, fixes and
constructive feedback.
* Read our [code of conduct](https://ubuntu.com/community/ethos/code-of-conduct):
As a community we adhere to the Ubuntu code of conduct.
* [Get support](https://discourse.charmhub.io/): Discourse is the go-to forum
for all Ops-related discussions, including around testing.
* Join our [online chat](https://matrix.to/#/#charmhub-charmdev:ubuntu.com):
Meet us in the #charmhub-charmdev channel on Matrix.
* [Report bugs](https://github.com/canonical/operator/issues): We want to know
about the problems so we can fix them.
* [Contribute docs](https://github.com/canonical/operator/blob/main/HACKING.md#contributing-documentation):
Get started on GitHub.
## Contributing and developing
Anyone can contribute to ops and `ops-scenario`. It's best to start by
[opening an issue](https://github.com/canonical/operator/issues) with a clear
description of the problem or feature request, but you can also
[open a pull request](https://github.com/canonical/operator/pulls) directly.
Read our [guide](./CONTRIBUTING.md) for more details on how to work on and
contribute to `ops-scenario`.
Currently, releases of `ops-scenario` are done in lockstep with releases of ops
itself, with matching minor and bugfix release numbers. The ops documentation
outlines how to create a new release.
Raw data
{
"_id": null,
"home_page": null,
"name": "ops-scenario",
"maintainer": "The Charm Tech team at Canonical Ltd.",
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "juju, test",
"author": null,
"author_email": "Pietro Pasotti <pietro.pasotti@canonical.com>",
"download_url": "https://files.pythonhosted.org/packages/eb/9f/03f711869e9b8ebc993a0686c38201ca738c61958db4795c65093d7e9cc9/ops_scenario-8.2.0.tar.gz",
"platform": null,
"description": "# ops-scenario, the unit testing framework for ops charms\n\n`ops-scenario` is a Python library that provides state-transition testing for\n[Ops](https://documentation.ubuntu.com/ops/latest/) charms. These tests are higher level than\ntypical unit tests, but run at similar speeds and are the recommended approach\nfor testing charms within requiring a full [Juju](https://juju.is) installation.\n\nTest are written in the arrange/act/assert pattern, arranging an object\nrepresenting the current Juju state, acting by emulating an event from Juju, and\nthen asserting on the (simulated) output Juju state.\n\n## Writing tests\n\nHere's a test that verifies that a unit is active after the `start` event, with a very minimal initial state:\n\n```python\nfrom ops import testing\n\n# 'src/charm.py' typically contains the charm class.\nfrom charm import MyCharm\n\ndef test_start():\n ctx = testing.Context(MyCharm)\n state_in = testing.State()\n state_out = ctx.run(ctx.on.start(), state_in)\n assert state_out.unit_status == testing.ActiveStatus()\n```\n\nMore comprehensive tests will include relations, containers, secrets, and other\ncomponents in the input state, and assertions against both the output state and\nthe context. The 'act' stage remains a simple single call, although additional\narguments may be required for the event, such as the relation or container that\ntriggered it. For example:\n\n```python\nimport pytest\nfrom ops import testing\n\nfrom charm import MyCharm\n\n@pytest.mark.parametrize(\n 'leader',\n [pytest.param(True, id='leader'), pytest.param(False, id='non-leader')],\n)\ndef test_(leader: bool):\n # Arrange:\n ctx = testing.Context(MyCharm)\n relation = testing.Relation('db', local_app_data={'hostname': 'example.com'})\n peer_relation = testing.PeerRelation('peer')\n container = testing.Container('workload', can_connect=True)\n relation_secret = testing.Secret({'certificate': 'xxxxxxxx'})\n user_secret = testing.Secret({'username': 'admin', 'password': 'xxxxxxxx'})\n config = {'port': 8443, 'admin-credentials': 'secret:1234'}\n state_in = testing.State(\n leader=leader,\n config=config,\n relations={relation, peer_relation},\n containers={container},\n secrets={relation_secret, user_secret},\n unit_status=testing.BlockedStatus(),\n workload_version='1.0.1',\n )\n\n # Act:\n state_out = ctx.run(ctx.on.relation_changed(relation), state_in)\n\n # Assert:\n assert testing.JujuLogLine(level='INFO', message='Distributing secret.') in ctx.juju_log\n peer_relation_out = state_out.get_relation(peer_relation.id)\n assert peer_relation_out.peers_data[0] == {'secret_id': relation_secret.id}\n```\n\nYou don't have to use pytest for your charm tests, but it's what we recommend.\npytest's `assert`-based approach is a straightforward way to write tests, and\nits fixtures are helpful for structuring setup and teardown.\n\n## Installation\n\nFor charm tests, install the testing framework by adding the `testing` extra of\nops in your unit testing environment. For example, in `pyproject.toml`:\n\n```toml\n[dependency-groups]\ntest = ['ops[testing]<4.0']\n```\n\nOps checks if `ops-scenario` is installed, and, if so, makes the classes\n(such as `Context`, `State`, and `Relation`) available in the `ops.testing`\nnamespace. Use `from ops import testing` rather than importing the `scenario`\npackage.\n\n`ops-scenario` supports the same platforms and Python versions as ops itself.\n\n## Documentation\n\n * To get started, work through our ['Write your first Kubernetes charm' tutorial](https://documentation.ubuntu.com/ops/latest/tutorial/from-zero-to-hero-write-your-first-kubernetes-charm/create-a-minimal-kubernetes-charm/#write-unit-tests-for-your-charm), following the instructions for adding\n unit tests at the end of each chapter.\n * When you need to write a test that involves specific ops functionality,\n refer to our [how-to guides](https://documentation.ubuntu.com/ops/latest/howto/)\n which all conclude with examples of tests of the ops functionality.\n * Use our extensive [reference documentation](https://documentation.ubuntu.com/ops/latest/reference/ops-testing/#ops-testing) when you need to know how each `testing` object works. These\n docs are also available via the standard Python `help()` functionality and in\n your IDE.\n\n[**Read the full documentation**](https://documentation.ubuntu.com/ops/latest/)\n\n## Community\n\n`ops-scenario` is a member of the Charming family. It's an open source project\nthat warmly welcomes community contributions, suggestions, fixes and\nconstructive feedback.\n\n* Read our [code of conduct](https://ubuntu.com/community/ethos/code-of-conduct):\n As a community we adhere to the Ubuntu code of conduct.\n* [Get support](https://discourse.charmhub.io/): Discourse is the go-to forum\n for all Ops-related discussions, including around testing.\n* Join our [online chat](https://matrix.to/#/#charmhub-charmdev:ubuntu.com):\n Meet us in the #charmhub-charmdev channel on Matrix.\n* [Report bugs](https://github.com/canonical/operator/issues): We want to know\n about the problems so we can fix them.\n* [Contribute docs](https://github.com/canonical/operator/blob/main/HACKING.md#contributing-documentation):\n Get started on GitHub.\n\n## Contributing and developing\n\nAnyone can contribute to ops and `ops-scenario`. It's best to start by\n[opening an issue](https://github.com/canonical/operator/issues) with a clear\ndescription of the problem or feature request, but you can also\n[open a pull request](https://github.com/canonical/operator/pulls) directly.\n\nRead our [guide](./CONTRIBUTING.md) for more details on how to work on and\ncontribute to `ops-scenario`.\n\nCurrently, releases of `ops-scenario` are done in lockstep with releases of ops\nitself, with matching minor and bugfix release numbers. The ops documentation\noutlines how to create a new release.\n",
"bugtrack_url": null,
"license": null,
"summary": "Python library providing a state-transition testing API for Operator Framework charms.",
"version": "8.2.0",
"project_urls": {
"Bug Tracker": "https://github.com/canonical/operator/issues",
"Homepage": "https://github.com/canonical/operator"
},
"split_keywords": [
"juju",
" test"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "68d66129b8852dd1e14e3332bc9299d4be39d3c5944f15dc80b2d541b97456ad",
"md5": "b041e204deb08eb9f01d394d0bfd495b",
"sha256": "35e0786b50f8ddd2067c78dbaff2d9219ef701cbebbbace50f8330aaa1fa91f0"
},
"downloads": -1,
"filename": "ops_scenario-8.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b041e204deb08eb9f01d394d0bfd495b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 63901,
"upload_time": "2025-08-27T22:58:34",
"upload_time_iso_8601": "2025-08-27T22:58:34.996807Z",
"url": "https://files.pythonhosted.org/packages/68/d6/6129b8852dd1e14e3332bc9299d4be39d3c5944f15dc80b2d541b97456ad/ops_scenario-8.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "eb9f03f711869e9b8ebc993a0686c38201ca738c61958db4795c65093d7e9cc9",
"md5": "ef6b18ec29798b9b5bf1488c2c646d3b",
"sha256": "33eb2ad46f6ee916747a2a0c5d6691454f23c9a7dc03dce24d0be4370c54d45a"
},
"downloads": -1,
"filename": "ops_scenario-8.2.0.tar.gz",
"has_sig": false,
"md5_digest": "ef6b18ec29798b9b5bf1488c2c646d3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 109715,
"upload_time": "2025-08-27T22:58:39",
"upload_time_iso_8601": "2025-08-27T22:58:39.463716Z",
"url": "https://files.pythonhosted.org/packages/eb/9f/03f711869e9b8ebc993a0686c38201ca738c61958db4795c65093d7e9cc9/ops_scenario-8.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-27 22:58:39",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "canonical",
"github_project": "operator",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "ops-scenario"
}