feature:test
Table of contents
- Introduction
- Testing every feature
- Choosing the levels
- Running only some tests
- Which copy is tested
- Testing two features together
- Testing in a neutral app
- Safety checks
- Advanced
- Reference
- Requirements
- See also
Introduction
feature:test runs the tests of one feature, or of every feature of the selected product. The tests run in the product’s web container, against the product’s app or against a neutral app that holds the feature and nothing of the product. Run it while you work on a feature and before you release it.
splent feature:test reports
🧪 Running tests for reports [unit, integration, functional]...
▶ splent_feature_reports
unit
4 passed in 0.15s
integration
3 passed in 1.39s
functional
1 passed in 1.22s
✅ Test runs (feature × level): 3 passed.
The summary counts pytest runs, one per feature and level. The test counts are pytest’s own lines above it. A failing run makes the command exit 1, so it stops a CI job.
Testing every feature
splent feature:test
Without a feature, it tests every feature the product declares for the current SPLENT_ENV. A feature with no copy on disk is skipped with a warning.
You can name a feature by its short name (notes), its package name (splent_feature_notes) or the entry the product declares (splent-io/splent_feature_notes@v1.12.0). A namespace or version you give must match the declaration, or the command stops.
Choosing the levels
splent feature:test --unit
splent feature:test reports --integration --functional
With no level flag it runs unit, integration and functional. Each flag picks one level, and you can combine them.
| Level | Tests | Needs |
|---|---|---|
--unit |
Services and helpers in isolation | Mocks only |
--integration |
Repositories, queries and relationships | The test database |
--functional |
Endpoints, redirects and rendered HTML | Flask’s test client and the database |
--e2e |
Whole browser flows | A running server and Selenium |
--load |
The test_*.py files under tests/load |
A running server |
The scaffolded locustfile.py is not a pytest file, so run it with splent locust <feature>. feature:create scaffolds one folder per level under tests/, and Test levels explains how to write each one.
Running only some tests
splent feature:test -k test_publish -v
-k passes a keyword to pytest, so only the tests whose name matches run. -v turns on pytest’s verbose output.
Which copy is tested
The tests come from the first copy found.
- The working copy at the workspace root,
<workspace>/<feature>/. - The product’s link under
features/to the version it declares. - That version in
.splent_cache, when the product has no link to it yet.
So a feature you are editing is tested from its working copy, even when the product pins a release. The command says so.
my_first_app pins splent_feature_notes at v1.18.0. This run tests the working copy in splent_feature_notes/ instead.
The app under test loads that same copy, through SPLENT_FEATURE_SOURCES, as Feature sources explains. Every other feature loads at the version the product declares. To test the pinned release itself, remove the working copy.
Testing two features together
splent feature:test reports --with notes --with mailer
When reports’ tests write notes through notes’ service, they run against the notes the product pins. If you are changing both, name the other one with --with. It loads from its working copy at the workspace root.
Loading splent_feature_notes from its working copy in splent_feature_notes/ instead of v1.20.0, the version my_first_app pins.
A feature named with --with must have a working copy, and in the product’s app it must also be declared.
Testing in a neutral app
splent feature:test card_payments
splent feature:test landing --neutral
A feature the product does not declare is tested in a neutral app. --neutral chooses it for one the product declares, so a template, error handler or setting of your product cannot make a shared feature’s tests pass or fail. The neutral app is a new product, written to a temporary directory and removed after the run.
splent_feature_card_payments is not declared in my_product, so its tests run in a neutral app.
Neutral app: the framework, these features and their default configuration.
Nothing of my_product reaches it but its test database.
splent_feature_sessions_on_disk v1.2.0 platform: where the sessions live
splent_feature_accounts v1.22.0 platform: who is signed in
splent_feature_my_theme v0.14.0 platform: the shells and the error pages
splent_feature_shop v0.5.0 required by card_payments
splent_feature_card_payments working copy under test
| The app holds | Taken from |
|---|---|
| The feature under test | Its working copy, or the release you name as <feature>@<version> |
| What it requires | requires.features, requires.features_manual and the base it refines, at the newest release on the forge that meets each specifier, and what those require in turn |
| What its tests import | Each feature an import, importlib.import_module or __import__ in its tests spells out, at its newest release. pytest.importorskip does not count |
| What its tests name | [tool.splent.test] features, for features the tests reach in a way no scan reads |
| The platform | One feature for sessions, sign-in and shells, unless a feature above already is it |
--with FEATURE |
Its working copy, in place of the release, or in addition. It need not be declared anywhere |
| The configuration | The framework’s defaults and each feature’s [tool.splent.config] |
Name the features only the tests need in the feature’s pyproject.toml, with the same short names and specifiers as the contract.
[tool.splent.test]
features = ["shop>=0.9.0", "notes_tags"]
The tests get nothing of the product but its test database. Releases are cloned into .splent_cache when missing. When the forge does not answer, the newest cached release that meets the specifier is used.
When a feature asks for a Python package the product’s image lacks, the run installs it in a virtual environment of its own, in the same temporary directory. The image is never written to. SPLENT’s own packages are never installed this way, so a feature that needs a newer framework than the container’s stops the run.
The platform
A product cannot start or render without three things the framework leaves to features. The neutral app holds one feature for each, found by what it does, never by its name.
| Capability | A feature is it when |
|---|---|
| Where the sessions live | Its config.py or __init__.py sets SESSION_TYPE |
| Who is signed in | It sets up Flask-Login’s LoginManager |
| The shells | It ships templates/public_base.html |
A feature already in the app wins. Otherwise the command takes a release from .splent_cache, the one that requires the fewest other features, then the first by name. When none provides a capability, it stops.
❌ The neutral app cannot be put together: no release in .splent_cache provides sessions (a feature that sets SESSION_TYPE), and the app does not start without one. Name one in [tool.splent.test] features, or give a working copy with --with.
Safety checks
Before pytest runs, the command refuses to touch anything but a test database.
| Check | Refuses when |
|---|---|
| Production | SPLENT_ENV=prod is in the product’s docker/.env, in the running web container, or in the command’s own environment there |
TESTING |
TESTING is not True in the Flask configuration |
| Test database | The database name lacks test, or is the development database (MARIADB_DATABASE) |
In a neutral app the database is MARIADB_TEST_DATABASE, which must be set, contain test and differ from MARIADB_DATABASE. Messages show the URI with the password masked.
❌ Non-test database in use: <database>
URI: <masked URI>
Tests require a database with 'test' in its name, other than
the development database (MARIADB_DATABASE).
If a production deployment is running, stop it first:
splent product:deploy --down
Advanced
From the CLI container,
feature:testruns itself again in<product>_webwithdocker exec, as the user who owns the workspace, so what the tests write stays yours. It exits with that run’s code, and stopping the CLI with Ctrl-C stops the tests too.
A product runs one test run at a time, since they share the test database. A second run prints
Another test run is using this product's test database. Waiting for it to finish...and waits.
A test that imports a model of a feature the app does not load fails only the query that meets it. See A model of a feature the app does not load.
Reference
Options
| Option | Description |
|---|---|
FEATURE_REF |
The feature to test. Without it, every declared feature |
--unit, --integration, --functional, --e2e, --load |
Run only these levels |
-k KEYWORD |
Only tests whose name matches, passed to pytest |
-v |
Verbose pytest output |
--with FEATURE |
Load FEATURE from its working copy too. Repeatable |
--neutral |
Test in a neutral app. Needs FEATURE_REF |
Messages
| Message | Meaning |
|---|---|
<level> skipped, no test_*.py files |
The level’s folder has no tests. A missing folder is skipped quietly |
No tests ran. No test_*.py files at the selected levels. |
Nothing ran. Exits 0 |
<feature> not found on disk |
No copy of a declared feature |
my_app declares splent_feature_notes pinned at v1.12.0, not v1.0.0. |
The version you gave is not the one declared |
There is no copy of <feature> in the workspace to test |
An undeclared feature with no working copy and no cached release. Clone it first |
--with <feature>, but <feature> has no working copy at the workspace root. |
Run splent feature:unlock <feature> |
End-to-end tests drive the product's running server |
--e2e in a neutral app, which is never served |
<requirer> requires shop>=2.0, and the release the neutral app loads is v1.22.0. |
No release meets every specifier |
Cannot run tests in production. |
Stop the deployment with splent product:deploy --down |
<product>_web is not running |
Start the product with splent product:up --dev |
Requirements
- A selected product (
SPLENT_APP), up in development mode withsplent product:up --dev. Its image installssplent_framework[dev], which brings pytest.
See also
product:testruns the product’s own tests.feature:createscaffolds the test folders.- Feature testing explains the levels and the fixtures.