Skip to main content

Juju CLI wrapper for charm integration testing

Project description

Jubilant, the joyful library for integration-testing Juju charms

Jubilant is a Python library that wraps the Juju CLI for use in charm integration tests. It provides methods that map 1:1 to Juju CLI commands, but with a type-annotated, Pythonic interface.

It was written to supersede the use of pytest-operator and python-libjuju for charm integration tests. Python-libjuju in particular has a complex and confusing API, and its use of async is unnecessary for testing.

Jubilant is currently in pre-release or "beta" phase (PyPI releases). Our intention is to release version 1.0.0 in May 2025.

Read the full documentation

Using Jubilant

Jubilant is published to PyPI, so you can install and use it with your favourite Python package manager:

$ pip install jubilant
# or
$ uv add jubilant

Because Jubilant calls the Juju CLI, you'll also need to install Juju.

To use Jubilant in Python code:

import jubilant

juju = jubilant.Juju()
juju.deploy('snappass-test')
juju.wait(jubilant.all_active)

Below is an example of a charm integration test. First we define a pytest fixture named juju which creates a temporary model and runs the test with a Juju instance pointing at that model. Jubilant'stemp_model context manager creates the model during test setup and destroys it during teardown:

# conftest.py
@pytest.fixture
def juju():
    with jubilant.temp_model() as juju:
        yield juju


# test_deploy.py
def test_deploy(juju: jubilant.Juju):        # Use the "juju" fixture
    juju.deploy('snappass-test')             # Deploy the charm
    status = juju.wait(jubilant.all_active)  # Wait till the app and unit are 'active'

    # Hit the Snappass HTTP endpoint to ensure it's up and running.
    address = status.apps['snappass-test'].units['snappass-test/0'].address
    response = requests.get(f'http://{address}:5000/', timeout=10)
    response.raise_for_status()
    assert 'snappass' in response.text.lower()

You don't have to use pytest with Jubilant, but it's what we recommend. Pytest's assert-based approach is a straight-forward way to write tests, and its fixtures are helpful for structuring setup and teardown.

Design goals

We designed Jubilant so it would:

  • Match the Juju CLI. Method, parameter, and response field names match the Juju CLI, with minor exceptions (such as "application" being shortened to "app").
  • Have a simple API. Higher-level operations will be in helper functions, not the main Juju class (the only exception being Juju.wait).
  • Not use async. This was a "feature" of python-libjuju that adds complexity and isn't needed for integration tests. In addition, most Juju CLI commands return quickly and complete asynchronously in the background.
  • Support Juju 3 and 4. The Juju team is guaranteeing CLI arguments and --format=json responses won't change between Juju 3.x and 4.x. When Juju 5.x arrives and changes the CLI, we'll keep the Jubilant API simple and match the 5.x CLI. However, we will consider adding a compatibility layer to avoid tests having to manually handle differences between 4.x and 5.x.

Contributing and developing

Anyone can contribute to Jubilant. 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.

Jubilant uses uv to manage Python dependencies and tools, so you'll need to install uv to work on the library. You'll also need make to run local development tasks (but you probably have make installed already).

After that, clone the Jubilant codebase and use make all to run various checks and the unit tests:

$ git clone https://github.com/canonical/jubilant
Cloning into 'jubilant'...
...
$ cd jubilant
$ make all
...
========== 107 passed in 0.26s ==========

To contribute a code change, write your fix or feature, add tests and docs, then run make all before you push and create a PR. Once you create a PR, GitHub will also run the integration tests, which takes several minutes.

Doing a release

To create a new release of Jubilant:

  1. Update the __version__ field in jubilant/__init__.py to the new version you want to release.
  2. Push up a PR with this change and get it reviewed and merged.
  3. Create a new release on GitHub with good release notes. The tag should start with a v, like v1.2.3. Once you've created the release, the publish.yaml workflow will automatically publish it to PyPI.
  4. Once the publish workflow has finished, check that the new version appears in the PyPI version history.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jubilant-0.1.0.tar.gz (19.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jubilant-0.1.0-py3-none-any.whl (18.6 kB view details)

Uploaded Python 3

File details

Details for the file jubilant-0.1.0.tar.gz.

File metadata

  • Download URL: jubilant-0.1.0.tar.gz
  • Upload date:
  • Size: 19.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for jubilant-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a9e4faf9ad024d46d92a12a5a312fd343d6c0ee81b08efa01046d9db98e510de
MD5 0a051024341b3634fa1fb1ad1b4ccb6d
BLAKE2b-256 f0769dad8e440f69be6f1a5e30964208dd77d353fca5b04e013886b82bc85176

See more details on using hashes here.

Provenance

The following attestation bundles were made for jubilant-0.1.0.tar.gz:

Publisher: publish.yaml on canonical/jubilant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jubilant-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: jubilant-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for jubilant-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 db3e0d9f1b2d5e549aecb3cc195ec036b9789acc89aad82aa6e88cf47c288c41
MD5 5f683668ea5bb6cc0ff1a20c9d444661
BLAKE2b-256 4263e40fda1ada6f88bf874baf82f23408190a668e1919a620d7e2095aab4f8f

See more details on using hashes here.

Provenance

The following attestation bundles were made for jubilant-0.1.0-py3-none-any.whl:

Publisher: publish.yaml on canonical/jubilant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page