Testing & Type Checking
Testing & Type Checking
Trilogy models are designed to be tested like software. There are three layers of confidence you can build, from cheapest to most thorough:
- Type checking — every concept is typed, and queries are checked at compile time, before any SQL is sent to a database.
- Unit tests — run scripts against mocked datasources with
trilogy unit. No database connection needed. - Integration tests — validate models against a real warehouse with
trilogy integration.
Type Checking
Every concept in a Trilogy model has a type. Beyond the standard primitives, type statements define custom types for concept declarations, enabling semantic typing and type aliases:
type usd float;
type email_address string;
key customer_id int;
property customer_id.email email_address;
property customer_id.balance usd;
Because queries reference typed concepts rather than raw columns, type errors surface when a script is parsed — not when a query fails at 2 AM. Custom types help document intent and enable tooling to provide better validation, code completion, and rendering.
There are standard types defined in the std. library imports included with Trilogy. These are suggested default integration targets, though users can create and extend their own.
Validation
Validate statements check the correctness and consistency of concepts or datasources in the current environment:
import customer as customer;
import order as order;
# Ensure model is consistent before running queries
validate all;
Validation checks that:
- Concept definitions are complete and consistent
- Datasource columns map to valid concepts
- Grain definitions are correct
- There are no circular dependencies
- Types are consistent
Unit Tests
The unit command runs unit tests on Trilogy scripts with mocked datasources. It always uses DuckDB internally, so no database connection or dialect argument is needed:
# Test single file
trilogy unit test_query.preql
# Test entire directory, in parallel
trilogy unit tests/ -p 4
This parses the script(s), validates datasources with mocked data, and reports validation results and any errors — a fast feedback loop for development and CI/CD.
Within a script, mock statements create temporary mock implementations of datasources or concepts for testing and development:
import customer as customer;
# Mock the customer datasource for testing
mock datasource customers;
# Now queries will use mock data
select
customer.id,
customer.name
;
Integration Tests
The integration command runs integration tests with real database connections. It validates that all datasources exist and are accessible — and that schemas are compatible with your model — but does not execute any data-modifying statements:
# Integration test against Postgres
trilogy integration tests/ postgres "postgresql://localhost/testdb"
# Integration test single file
trilogy integration query.preql duckdb
A Typical CI/CD Flow
- On every commit:
trilogy unit .— parse, type-check, and validate all scripts against mocked data. - After deployment:
trilogy integration . <dialect>— verify the deployed warehouse matches the model.