`scenario 7` is out!

To all charming testers out there! Scenario 7.0(.1) is out and packed with awesome (breaking) changes and features and (promises of) a new, stable API that will not be changing at a breakneck pace anymore now that my work is being taken under the umbrella of the ops framework and their backwards-compatibility-oriented maintainers.

For a migration guide and a changelog of the most relevant API changes that you should head over here.

For example PRs of what a complete migration of a complex test suite looks like, look at:

2 Likes

Somewhat belatedly (apologies!), some more detailed release notes:

Features

  • Support for testing Pebble check events (much nicer support than in Harness!)
  • Container exec mocking has more of the functionality that Harness has - in particular, you can match against a command prefix rather than the exact command, and you can inspect a list of the commands that the charm exec’d (along with how that was done: stdin, users, environment, etc)
  • StoredState now has some consistency checks

API adjustments

Specifying your event

The biggest change is in how you specify the event to run. Previously, this was either a string or pulled via a State component like Relation or you manually made an Event object. Now, this should feel pretty similar to how you observe events in ops, except that you get better autocomplete and type annotations.

Simple events look like:

ctx.run(ctx.on.start(), State())

And when there’s some additional information that the event needs (that would come from the Juju environment or hook tools) they look like:

ctx.run(ctx.on.relation_changed(my_relation), State())

Action events have this new approach, too, without needing a separate method, like:

ctx.run(ctx.on.action("create-admin"), State())

We’ve also moved the old .manager into the Context object itself, so when you need access to the charm, you can do it like this:

with ctx(ctx.on.update_status(), State()) as mgr:
    mgr.charm.do_something()
    mgr.run()

Goodbye magic numbers

The next biggest change is that the various collections in the State are frozensets instead of lists. Relations, containers, storages, and so on don’t really have any logical ordering, and the Scenario state should be treated as immutable, so frozenset is a better fit.

Python doesn’t have a nice frozenset literal syntax, and we dont’ want people to have to write things like relations=frozenset({rel1, rel2}), so when you’re constructing the state, you can pass in any iterable type (a list, as before, or tuple, or - ideally - set) and it’ll get converted to a frozenset for you.

This means no more “magic numbers” in your asserts, like assert state.relations[0].x = y. Instead, these collections have get_ methods, so you can write things like assert state.get_relation(rel1.id).x = y.

Keyword arguments

Most of the Scenario classes now expect at least some arguments to be passed as keywords, where there wasn’t really any logical ordering. In most cases, your Scenario tests will have been doing this already.

Simpler secrets

It’s much simpler to test events whose handlers manage Juju secrets - in particular: no more managing revision numbers.

And more

There’s some additional renaming/cleaning-up that’s best read about in the upgrading guide that @ppasotti mentioned, which also has more detailed examples of the above.

Internal improvements of note

  • The code blocks in the README are tested (with tox -e test-readme), although there are some limitations here.
  • We explicitly test against Python 3.12 (3.13 soon!) and stopping testing against Pythons older than 3.8.
2 Likes