Scenario

Source: GitHub

See also: How to write a functional test with Scenario

Scenario (scenario) is a testing framework for charms written with Ops (ops). It excels at functional testing of charm code, making it ideal to write:

  • state-transition tests
  • contract tests

The core idea of Scenario is that a charm is best conceived of as an opaque input-output function, where:

  • the input is a monolithic data structure called State, representing the data that Juju makes available to the charm (as represented by the Context object) at runtime
  • the output is the State after the charm has been triggered to execute by some Event and has had a chance to interact with the input State.

Scenario tests are written by declaring an initial state, then running the charm context with that state and an event as parameters to obtain the output state. You can then write assertions to verify that the output state looks like you expect it to; e.g. that a databag has been updated to contain a certain piece of data, or that the application status has been set to blocked, etc…

Example

An example scenario test using Relation might look like:

import ops

from scenario import Relation, State, Context


# This charm copies over remote app data to local unit data
class MyCharm(ops.CharmBase):
    ...

    def _on_event(self, e):
        rel = e.relation
        assert rel.app.name == 'remote'
        assert rel.data[self.unit]['abc'] == 'foo'
        rel.data[self.unit]['abc'] = rel.data[e.app]['cde']


def test_relation_data():   
    # use scenario.Context to declare what charm we are testing 
     ctx = Context(MyCharm,
                  meta={"name": "foo"})

    # ARRANGE: declare the input state
    state_in = State(relations=[
        Relation(
            endpoint="foo",
            interface="bar",
            remote_app_name="remote",
            local_unit_data={"abc": "foo"},
            remote_app_data={"cde": "baz!"},
        ),
    ])

    # ACT: run an event in the context and pass the input state to it
    state_out = ctx.run('start', state_in)
  
    # ASSERT that the output state looks the way you expect it to
    assert state_out.relations[0].local_unit_data == {"abc": "baz!"}
    # you can directly compare State data structures to assert a certain delta
    assert state_out.relations == [
        Relation(
            endpoint="foo",
            interface="bar",
            remote_app_name="remote",
            local_unit_data={"abc": "baz!"},
            remote_app_data={"cde": "baz!"},
        ),
    ]

Last updated 6 months ago. Help improve this document in the forum.