Testing

You can certainly just directly call any invoke functions directly, manually constructing and providing arguments yourself.

You can also just directly call cappa.parse or cappa.invoke yourself, with relatively little effort.

However, Cappa comes with a built in CommandRunner which is meant to reduce the verbosity of testing CLI commands and in overriding upstream Dep. With it, you can centralize any always-used options, such that only the options which vary from test to test are provided inside the test bodies…

Pytest

Cappa does not come with a built in pytest fixture because we assume that any test suite which might benefit from a fixture will likely have other fixture dependencies. Most users will want to customize the construction of their CommandRunner, if they’re going to use one at all.

It is very straightforward to define you own fixture to produce an appropriately configured runner.

For example, let us describe a base CLI which depends upon an Explicit Dependency on a configuration dictionary which pulls data from the environment; which you need to override in a fixture to work with the rest of your testing structure.

Note

See Invoke dependency overrides for additional details.

Your existing code might look like this:

import os
from typing import Annotated

import cappa

def config() -> dict:
    return {
        "env": os.getenv("ENV")
        "foo": os.getenv("FOO")
    }


def fn(config: Annotated[dict, cappa.Dep(config)]):
    print(config)

@cappa.command(invoke=fn)
class CLI:
    name: str

In your tests, you’ve decided you want to hard-code a specific alternative config value. You could define a pytest fixture like so:

import pytest
from cappa.testing import CommandRunner

from package import CLI, config

@pytest.fixture
def runner():  # Note `runner` could itself depend on other fixtures, in more complex scenarios
    return CommandRunner(CLI, deps={config: {"env": "test", "foo": "bar"}})

# OR
def stub_config() -> dict:
    return {
        "env": "test"
        "foo": "bar"
    }

@pytest.fixture
def runner():
    return CommandRunner(CLI, deps={config: cappa.Dep(stub_config)})

Then your tests will be able to omit most configuration, except for the item under test:

def test_foo(runner: CommandRunner):
    runner.invoke('name!')