How tests work
A test file defines mock inputs and expected outputs using CTEs. SQLBuild substitutes the mock data into the real model SQL, executes it, and compares the result against the expected CTE usingEXCEPT queries. Zero mismatched rows means the test passes.
- Mocks the
raw__orderssource with the__source__raw__ordersCTE - Runs the real
stg_ordersmodel SQL with the mock substituted in - Compares the output against
__expected__stg_orders - Passes if row counts match and there are zero mismatched rows
SELECT 1 is required as a ceremonial closing statement.
CTE conventions
| Prefix | Purpose |
|---|---|
__source__<name> | Mock a source. Replaces __source("<name>") in the model SQL. |
__ref__<name> | Mock a model. Replaces __ref("<name>") in the model SQL. |
__seed__<name> | Mock a seed. Replaces __seed("<name>") in the model SQL. |
__expected__<name> | Define expected output for a model. SQLBuild resolves the model’s real SQL and compares against this. |
__assert__<name> | Zero-row assertion. Passes if the query returns no rows; fails with the returned rows as diagnostics. |
__macro__<name> | Mock a macro. Replaces every @<name>(...) call with the mock value. |
Chaining across models
Tests can span multiple models in a single file. Mock your sources, define an expected output for the model you care about, and SQLBuild automatically resolves every intermediate model using its real SQL.Mocking refs and seeds
You can mock models directly with__ref__<name> and seeds with __seed__<name>, not just sources. This skips the model’s real SQL (or the seed’s real CSV data) and provides controlled data instead:
__source__, __ref__, and __seed__ mocks in the same test. As long as every leaf dependency is satisfied (either by a source mock, a ref mock, a seed mock, or by being in the expected chain), the test resolves.
Multiple expected models
A single test can assert on multiple models. SQLBuild resolves and compares each one independently:stg_orders feeds into fact_orders which feeds into dim_customers), SQLBuild resolves them in dependency order, using the output of earlier steps as input to later ones.
Macro-powered mocks
Because unit tests are written in SQL, they support macro calls. This lets you write reusable mock generators instead of copy-pasting mock data across test files:@mock_orders() call expands at compile time to whatever SQL the Python macro function returns.
Macro mocking
When a model uses macros that you want to control in tests (e.g. target-specific logic, dynamic SQL generation), you can override their output with__macro__<name> CTEs:
__macro__ mock is defined, every call to @country_filter(...) in any model SQL resolved by the test is replaced with the mock value (country_code = 'US'). The macro’s actual Python function is not called, and the arguments are ignored.
The mock value must be a single SELECT with one string literal. Use doubled single quotes for quotes within the value (standard SQL escaping).
This is useful for:
- Testing models that use target-specific macros without depending on target config
- Controlling dynamic SQL generation to produce predictable test inputs
- Isolating model logic from macro implementation details
Assertions
Unit tests can include__assert__<name> CTEs for property-based checks. An assertion passes if the query returns zero rows - any returned rows are failing examples.
__expected__ CTEs in the same test, or used on their own. They are useful when the natural check is “no rows should violate this rule” rather than “the output should exactly equal these rows” - for example, duplicate checks, negative-value constraints, or conditional business rules.
During sqb test and sqb build, assertion results appear as nested check rows alongside expected comparisons.
Test modes
By default,TEST() runs in model mode - mocking sources/refs and comparing model outputs. Three additional modes let you test reusable logic directly without needing a model chain.
Macro tests
Test macro output by calling the macro in__macro_actual__ and comparing against __macro_expected__:
sqb build, macro tests run before any model that uses the tested macro.
UDF tests
Test scalar UDFs by calling them in__udf_actual__ and comparing against __udf_expected__:
sqb build, UDF tests run after the function is created but before any model that uses it.
Table function tests
Test table functions by calling them in__table_fn_actual__ and comparing against __table_fn_expected__:
Mode rules
Each mode has strict CTE validation:| Mode | Actual CTE | Expected CTE | Allowed |
|---|---|---|---|
model (default) | existing model-chain syntax | __expected__<model> | __source__, __ref__, __seed__, __assert__, __macro__ |
macro | __macro_actual__ | __macro_expected__ | Helper CTEs, @macro() calls in actual |
udf | __udf_actual__ | __udf_expected__ | Helper CTEs, __udf() calls in actual |
table_fn | __table_fn_actual__ | __table_fn_expected__ | Helper CTEs, __table_fn() calls in actual |
__source__ in a macro test or __macro_actual__ in a model test will produce a clear error pointing you to the right mode. Expected CTEs must not call macros, UDFs, or table functions - they should be independent, inspectable expected data.
Multiple tests per file
A single test file can contain multipleTEST() blocks. Each block must have a unique name:
name field. Files with multiple tests require names on every block.
Test placement
Place unit test files undertests/unit/ in your project directory. SQLBuild discovers all .sql files in this directory recursively.
Running tests
Tests run automatically duringsqb build in DAG order, before their target model is materialized.
Run tests standalone:

