> For the complete documentation index, see [llms.txt](https://v2.dataos.info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://v2.dataos.info/build/v1/productize/unit-test.md).

# Unit Test

A unit test checks a model's transformation logic. It runs the model's SQL against fixtures: inputs and expected outputs that you write by hand. No warehouse is involved.

This is different from an [assertion](/build/v1/productize/assertions.md). An assertion checks real data after a model runs. A test checks the model's logic before that, using fixed, fake data you control.

Tests fit alongside the other data quality tools in Vulcan. Here's the mental model to hold onto: unit tests validate logic, audits define a rule, assertions attach that rule to a model and block bad data, and DQ checks watch data over time and warn without blocking.

Applied to tests specifically: a test proves the model's SQL produces the right output for a given input. An audit is a reusable rule. An assertion attaches that rule to a model. A [DQ check](/build/v1/productize/data-quality.md) monitors quality over time.

Tests run on demand, such as in a CI/CD pipeline. They also run automatically whenever you apply a [plan](/build/v1/productize/plan-and-run.md).

## Creating tests

Tests live in YAML files in the `tests/` folder. The filename must start with `test` and end in `.yaml` or `.yml`. You can put multiple tests in one file.

At minimum, a test needs 3 keys:

* `model`: the model you are testing.
* `inputs`: mock data for upstream dependencies (what goes in).
* `outputs`: expected results from the model's query (what should come out).

`orders-analytics` has 2 tests:

* `test_fct_daily_sales_excludes_cancelled_orders` checks a core business rule: canceled orders must not count toward revenue in `silver.fct_daily_sales`. The `@revenue_order_filter` macro enforces this rule.
* `test_rfm_customer_segmentation_champion` checks that a customer with top recency, frequency, and monetary scores lands in the Champions segment in `gold.rfm_customer_segmentation`.

{% code overflow="wrap" %}

```yaml
test_fct_daily_sales_excludes_cancelled_orders:
  model: silver.fct_daily_sales
  inputs:
    bronze.orders:
      - order_id: 1
        customer_id: 10
        order_date: '2026-01-01 10:00:00'
        warehouse_id: 100
        order_status: Shipped
      - order_id: 2
        customer_id: 10
        order_date: '2026-01-01 11:00:00'
        warehouse_id: 100
        order_status: Cancelled
    bronze.customers:
      - customer_id: 10
        region_id: 1
        name: Avery Stone
        email: avery.stone@example.com
        signup_date: '2025-12-01'
    bronze.regions:
      - region_id: 1
        region_name: North
    bronze.order_items:
      - order_id: 1
        item_id: 1
        product_id: 200
        quantity: 2
        unit_price: 10.00
      - order_id: 2
        item_id: 1
        product_id: 200
        quantity: 5
        unit_price: 10.00
    bronze.products:
      - product_id: 200
        supplier_id: 300
        name: Widget Basic
        category: Widgets
        price: 10.00
  outputs:
    query:
      partial: true
      rows:
        - customer_id: 10
          total_orders: 1
          total_revenue: 20.00
```

{% endcode %}

Two orders go in: one Shipped, one Canceled.

The Canceled order's items (quantity 5, value 50.00) never reach the output. The single expected row shows `total_orders: 1` and `total_revenue: 20.00`, from the Shipped order alone.

`partial: true` means Vulcan only checks the listed columns. It ignores the model's other output columns.

{% hint style="warning" %}
Expected output columns must appear in the same order as the model's `SELECT`. A test that fails even though the data looks correct is often just a column-order mismatch, not a logic bug.
{% endhint %}

Set `<test_name>.schema` to name the schema where Vulcan creates the test's fixture views, if you need it somewhere other than the default temp schema.

{% hint style="info" %}
Shortcut: if `rows` is the only key under an input model, you can omit the `rows:` level entirely. For example, `bronze.regions: [{region_id: 1, region_name: North}]` is equivalent to `bronze.regions: {rows: [{region_id: 1, region_name: North}]}`.
{% endhint %}

## Key capabilities

* **Multiple dependencies** - provide mock rows for every upstream model the target joins, listed side by side under `inputs`, exactly as the example above does for five bronze tables.
* **Incremental models** - set `vars.start` and `vars.end` to control the `@start_ds`/`@end_ds` macros an incremental model reads.
* **Testing CTEs** - assert on an intermediate CTE via `outputs.ctes.<cte_name>.rows`, so you can debug a complex query step by step instead of only checking the final result.
* **Data formats** - input rows default to YAML. Use `format: csv` (tune with `csv_settings`), a SQL `query`, or `path` to an external file instead. `query` and `rows` are mutually exclusive on the same input.
* **Partial matching** - set `partial: true`, at either `outputs.partial` or `outputs.query.partial`, to check only the columns you list; Vulcan ignores the rest.
* **Freezing time** - set `vars.execution_time` so a model using `CURRENT_TIMESTAMP`/`CURRENT_DATE` produces the same result every run.
* **Explicit column types** - if Vulcan infers the wrong type for a mock column, declare it under `inputs.<model>.columns`.
* **Automatic generation** - bootstrap a test from real data with `vulcan create_test <model> --query <upstream> "<sql>"`, then refine the fixture by hand.

For the full syntax of each of these, see the reference link at the end of this page.

## Running tests

```bash
# Run all tests
vulcan test

# Run a specific file
vulcan test tests/test_fct_daily_sales.yaml

# Run a single test with the :: syntax
vulcan test tests/test_fct_daily_sales.yaml::test_fct_daily_sales_excludes_cancelled_orders
```

In `orders-analytics`, `vulcan test` runs both tests:

```
test_fct_daily_sales_excludes_cancelled_orders          PASS
test_rfm_customer_segmentation_champion                 PASS
```

A failing test prints a diff of expected (`exp`) versus actual (`act`) rows. Fix the model or the expectation, then re-run.

A test proves the model's logic is right before any warehouse run ever sees it. For the full test-key reference, `csv_settings` options, and troubleshooting tips, see [References → Vulcan → Quality → Unit Test](https://v2.dataos.info/references/resources/vulcan/quality/tests).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://v2.dataos.info/build/v1/productize/unit-test.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
