Write scenario tests for your charm

From Zero to Hero: Write your first Kubernetes charm > Write scenario tests for your charm

See previous: Write unit tests for your charm

This document is part of a series, and we recommend you follow it in sequence. However, you can also jump straight in by checking out the code from the previous branches:

git clone https://github.com/canonical/juju-sdk-tutorial-k8s.git
cd juju-sdk-tutorial-k8s
git checkout 08_unit_testing
git checkout -b 09_scenario_testing

In the previous chapter we checked the basic functionality of our charm by writing unit tests.

However, there is one more type of test to cover, namely: functional tests.

In the charming world the current recommendation is to write functional tests with the ‘scenario’ model popularised by the ops-scenario library.

Ops-scenario is a state-transition testing SDK for operator framework charms.

In this chapter you will write a scenario test to check that the get_db_info action that you defined in an earlier chapter behaves as expected.


  1. Prepare your test environment
  2. Prepare your test directory
  3. Write your scenario test
  4. Run the test
  5. Review the final code

Prepare your test environment

Install ops-scenario:

pip install ops-scenario

In your project root’s existing tox.ini file, add the following:


description = Run scenario tests
deps =
    -r {tox_root}/requirements.txt
commands =
    coverage run --source={[vars]src_path} \
                 -m pytest \
                 --tb native \
                 -v \
                 -s \
                 {posargs} \
    coverage report

Prepare your test directory

By convention, scenario tests are kept in a separate directory, tests/scenario. Create it as below:

mkdir -p tests/scenario
cd tests/scenario

Write your scenario test

In your tests/scenario directory, create a new file test_charm.py and add the test below. This test will check the behavior of the get_db_info action that you set up in a previous chapter. It will first set up the test context by setting the appropriate metadata, then define the input state, then run the action and, finally, check if the results match the expected values.

from charm import FastAPIDemoCharm
from scenario import Relation, State, Context, Container, Action

import unittest
import unittest.mock

class TestCharm(unittest.TestCase):
    def test_get_db_info_action(self, *_):
        # use scenario.Context to declare what charm we are testing
        ctx = Context(
                "name": "demo-api-charm",
                "containers": {"demo-server": {}},
                "peers": {"fastapi-peer": {"interface": "fastapi_demo_peers"}},
                "requires": {
                    "database": {
                    "interface": "postgresql_client",
                "options": {
                    "server-port": {
                    "default": 8000,
                "get-db-info": {
                    "params": {"show-password": {"default": False, "type": "boolean"}}

        # declare the input state
        state_in = State(
                        "endpoints": "",
                        "username": "foo",
                        "password": "bar",
                Container(name="demo-server", can_connect=True),

        #run the action with the defined state and collect the output.
        action = Action("get-db-info", {"show-password": True})
        action_out = ctx.run_action(action, state_in)

        assert action_out.results == {
            "db-host": "",
            "db-port": "5432",
            "db-username": "foo",
            "db-password": "bar",

Run the test

In your Multipass Ubuntu VM shell, run your scenario test as below:

ubuntu@charm-dev:~/fastapi-demo$ tox -e scenario     

You should get an output similar to the one below:

scenario: commands[0]> coverage run --source=/home/benjamin/work/juju-sdk-tutorial-k8s/src -m pytest --tb native -v -s /home/benjamin/work/juju-sdk-tutorial-k8s/tests/scenario
=============================================================================================================================================================================== test session starts ===============================================================================================================================================================================
platform linux -- Python 3.10.12, pytest-7.4.2, pluggy-1.3.0 -- /home/benjamin/work/juju-sdk-tutorial-k8s/.tox/scenario/bin/python
cachedir: .tox/scenario/.pytest_cache
rootdir: /home/benjamin/work/juju-sdk-tutorial-k8s
collected 1 item                                                                                                                                                                                                                                                                                                                                                                  

tests/scenario/test_charm.py::TestCharm::test_get_db_info_action PASSED

================================================================================================================================================================================ 1 passed in 0.21s ================================================================================================================================================================================
scenario: commands[1]> coverage report
Name           Stmts   Miss  Cover
src/charm.py     118     51    57%
TOTAL            118     51    57%
  scenario: OK (0.51=setup[0.03]+cmd[0.40,0.07] seconds)
  congratulations :) (0.54 seconds)

Congratulation, you have written your first scenario test!

Review the final code

For the full code see: 09_scenario_testing

For a comparative view of the code before and after this doc see: Comparison

See next: Write integration tests for your charm

Contributors: @bschimke95

Last updated a month ago.