Skip to main content

ops-scenario, the unit testing framework for ops charms

ops-scenario is a Python library that provides state-transition testing for Ops 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 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:

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:

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:

[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, 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 which all conclude with examples of tests of the ops functionality.
  • Use our extensive reference documentation 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

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: As a community we adhere to the Ubuntu code of conduct.
  • Get support: Discourse is the go-to forum for all Ops-related discussions, including around testing.
  • Join our online chat: Meet us in the #charmhub-charmdev channel on Matrix.
  • Report bugs: We want to know about the problems so we can fix them.
  • Contribute docs: Get started on GitHub.

Contributing and developing

Anyone can contribute to ops and ops-scenario. It's best to start by opening an issue with a clear description of the problem or feature request, but you can also open a pull request directly.

Read our guide 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.

Metadata

Release files for ops-scenario 8.8.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ops-scenario 8.8.2
File Size Uploaded
ops_scenario-8.8.2.tar.gz 87.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ops-scenario 8.8.2
File Interpreter ABI Platform
ops_scenario-8.8.2-py3-none-any.whl Python 3 none any Details

Total release size: 161.9 kB

Release files / ops_scenario-8.8.2.tar.gz

Download URL ops_scenario-8.8.2.tar.gz
Size 87.0 kB
Tags Source
SHA-256 checksum
How to use checksums
4d9c8371ae02064352f852dda4cccd0fb825b8184f26ae2150c8b5bc1032c6b9
BLAKE2b-256 checksum
How to use checksums
1e06fc2da6f27a9831b11ed55420b0d65720f6b96ee324fcea0e699e4e01b7ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / ops_scenario-8.8.2-py3-none-any.whl

Download URL ops_scenario-8.8.2-py3-none-any.whl
Size 74.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e2a8edc04e169739e0769a4cec0acbd6e0d82d771a3e5617223a9f537f3128a
BLAKE2b-256 checksum
How to use checksums
1515abe6b080341cd6db8c9d1d7124903c48361f3cf783c8d8a08043560f1e1a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

8.8.3

2 release files

This release

8.8.2 This release

2 release files

8.8.1

2 release files

8.8.0

2 release files

8.7.1

2 release files

8.7.0

2 release files

8.6.0

2 release files

8.5.2

2 release files

8.5.1

2 release files

8.5.0

2 release files

8.4.0

2 release files

8.3.1

2 release files

8.3.0

2 release files

8.2.0

2 release files

8.1.0

2 release files

8.0.0

2 release files

7.23.5

2 release files

7.23.4

2 release files

7.23.2

2 release files

7.23.1

2 release files

7.23.0

2 release files

7.22.0

2 release files

7.21.0

2 release files

7.20.0

2 release files

7.19.4

2 release files

7.2.0

2 release files

7.1.3

2 release files

7.1.2

2 release files

7.1.1

2 release files

7.1.0

2 release files

7.0.5

2 release files

7.0.4

2 release files

7.0.3

2 release files

7.0.2

2 release files

7.0.1

2 release files

7.0.0

2 release files

6.1.7

2 release files

6.1.6

2 release files

6.1.5

2 release files

6.1.4

2 release files

6.1.3

2 release files

6.1.2

2 release files

6.1.1

2 release files

6.1.0

2 release files

6.0.5

2 release files

6.0.4

2 release files

6.0.3

2 release files

6.0.2

2 release files

6.0.1

2 release files

6.0

2 release files

5.8.1

2 release files

5.8

2 release files

5.7.1

2 release files

5.7

2 release files

5.6.2

2 release files

5.6.1

2 release files

5.6

2 release files

5.5

2 release files

5.4.1

2 release files

5.4

2 release files

5.3.1

2 release files

5.3

2 release files

5.2.2

2 release files

5.2.1

2 release files

5.2

2 release files

5.1.3

2 release files

5.1.2

2 release files

5.1.1

2 release files

5.1.0

2 release files

5.0

2 release files

4.0.4

2 release files

4.0.3

2 release files

4.0.2

2 release files

4.0.1

2 release files

4.0

2 release files

3.1

2 release files

3.0.2

2 release files

3.0.1

2 release files

2.2

2 release files

2.1.3

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.5

2 release files

2.0.4

2 release files

2.0.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page