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!')