Test your SPARQL queries and transformations with precision, using Given-When-Then principles.
# One spec. Given data. When query. Then result. :my_test must:given [ a must:FileDataset ; must:file "test/data/given.ttl" ] ; must:when [ a must:TextSparqlSource ; must:queryText "CONSTRUCT { ?s ?p ?o } WHERE { ?s a ex:Person ; ?p ?o }" ; must:queryType must:ConstructSparql ] ; must:then [ a must:FileDataset ; must:file "test/data/expected.ttl" ] .
Define your data, run your query, check the result. All in Turtle.
Add --mustrd to your pytest command. Specs become tests. Results show in your runner, CI, and VS Code.
Run specs against embedded RDFLib, GraphDB, or Anzo. Same specs, different engines. Swap with configuration, not code.
Inspired by Cucumber. Define concrete examples of what your query should do, not abstract assertions about what it might.
SHACL validates data structure. mustrd validates data transformations. Your query produces the right triples? mustrd checks.
Every spec follows the same shape. Load data, run a query, check the output.
The input dataset. A Turtle file, inline statements, an Anzo graphmart, or inherited state.
must:given [ a must:FileDataset ; must:file "data/input.ttl" ] ;
The SPARQL action. Inline text, a file reference, or a query builder source. SELECT, CONSTRUCT, or UPDATE.
must:when [ a must:FileSparqlSource ; must:file "sparql/enrich.rq" ; must:queryType must:UpdateSparql ] ;
The expected result. An RDF graph, a table of bindings, CSV/Excel, or an empty result.
must:then [ a must:FileDataset ; must:file "data/expected.ttl" ] .
mustrd validates across SPARQL's full query surface.
| Type | What it does | Result compared as |
|---|---|---|
| SELECT | Return variable bindings | Table (pandas DataFrame) |
| CONSTRUCT | Build an RDF graph | Graph isomorphism |
| UPDATE | INSERT/DELETE triples | Modified graph comparison |
| ASK | Boolean existence check | True/false |
| DESCRIBE | Describe a resource | Graph isomorphism |
Write specs once. Run them against any supported engine.
| Engine | Protocol | Notes |
|---|---|---|
| RDFLib | Embedded (in-memory) | Default. No server needed. Zero config. |
| GraphDB | HTTP SPARQL endpoint | Repository-based. Optional named graphs. |
| Anzo | HTTP REST API | Graphmart layers, query builders, AnzoGraph. |
Givens and thens support multiple data formats.
| Type | Description |
|---|---|
| FileDataset | RDF file — Turtle, NTriples, N3, RDF/XML, TriX |
| StatementsDataset | Inline reified RDF statements in the spec |
| TableDataset | Inline tabular bindings for SELECT results |
| OrderedTableDataset | Tabular data with row ordering (ORDER BY) |
| FolderDataset | File path passed as a runtime argument |
| EmptyTable / EmptyGraph | Expect no results |
| InheritedDataset | Keep existing graph state (chain specs) |
Specs appear as tests in the Test Explorer. Click to run, see diffs on failure.
// settings.json { "python.testing.pytestArgs": [ "--mustrd", "--md=junit/github_job_summary.md", "--config=test/test_config_local.ttl" ] }
A run's output is RDF first: DQV quality measurements over your ontology, a per-term breakdown, competency-question assertions, and a result per test — all with stable IRIs, so runs merge and diff in a triplestore.
The HTML report is rendered from that graph, in the browser, by a
single self-contained file. No build step, no CDN, no server: attach it to a
CI run or open it from disk. Drop another run's .ttl onto the
page to load it.
The same engine, the same configuration file, the same reports. Pick whichever fits where you are.
Specs become tests in your existing suite, so they run in CI and appear in the VS Code Test Explorer next to everything else.
pip install mustrd pytest --mustrd \ --config=mustrd-config.ttl \ --viewer=report.html
No pytest. Useful in a pipeline step, a pre-commit hook, or anywhere a test runner would just be in the way. Exits non-zero if a spec fails.
pip install mustrd mustrd run --config mustrd-config.ttl mustrd report --config mustrd-config.ttl \ --viewer report.html
mustrd run executes the specs and prints a result table.
mustrd report does the same and writes the artefacts you ask for
— --viewer for the HTML page above, --md for
Markdown, --term-coverage-rdf / --results-rdf for
the graph itself. Both take the same flags as the plugin.
Windows is a tested platform, not an afterthought — CI runs the whole suite on it against Python 3.11, 3.12 and 3.13. The reports use box-drawing characters and emoji, and a Windows console still defaults to cp1252 where printing those throws, so the CLI reconfigures its own streams to UTF-8 on startup. That failure mode is handled rather than left to you.
Nothing special to do. Paths inside a config resolve relative to the config file rather than the working directory, so drive letters and backslashes are fine and you can run from anywhere.
pytest --mustrd --config=test\config.ttl mustrd report --config test\config.ttl \ --viewer report.html
Enterprise builds commonly block the bare .exe shims pip
installs into Scripts\. The package installs fine, but the
mustrd command won't start. Run the module through the venv's
interpreter instead.
python -m venv .venv .venv/Scripts/python -m pip install mustrd .venv/Scripts/python -m mustrd report \ --config config.ttl
python -m mustrd is exactly equivalent to the mustrd
command — same entry point, same flags. It exists for this.
More
in the README.
mustrd CLI (run & report without pytest): implementedpip install mustrd
pytest --mustrd --config=test/mustrd_configuration.ttl