> 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/stage-2-productize/define-the-contract/tests.md).

# Tests

Tests let you verify model logic using mock data. No warehouse connection required. Write a test once, run it locally, and catch regressions early before pushing changes.

Tests in Vulcan are YAML files in the `tests/` directory. Each test provides mock input data, runs the model against it, and asserts on the output.

***

## How tests work

1. You define mock rows for the upstream tables your model depends on.
2. `vulcan test` applies the model's SQL to the mock data in an isolated DuckDB instance.
3. The test asserts that the output matches expected rows.
4. If the output does not match, the test fails with a diff showing what was expected vs what was actually produced.

No warehouse is written to. The test runs entirely in memory.

***

## Test file structure

```yaml
<test_name>:
  model: <schema.table_name>
  inputs:
    <schema.upstream_table>:
      - column1: value1
        column2: value2
  outputs:
    query:
      rows:
        - output_column1: expected_value1
      partial: true   # optional: only check listed columns, ignore the rest
```

***

## Example 1: Verifying cancelled orders are excluded from revenue

`orders-analytics` has a core business rule: Cancelled orders are excluded from all revenue calculations using the `@revenue_order_filter` macro. This test verifies that rule.

`tests/test_fct_daily_sales.yaml`:

{% 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
    bronze.shipments:
      - shipment_id: 500
        order_id: 1
        shipped_date: "2026-01-02"
        carrier: DHL
  outputs:
    query:
      rows:
        - order_date: "2026-01-01"
          region_id: 1
          region_name: North
          customer_id: 10
          product_id: 200
          category: Widgets
          total_orders: 1
          total_items_sold: 2
          total_revenue: 20.00
          avg_order_value: 20.00
          total_shipments: 1
          shipment_rate: 1.0000
      partial: true
```

{% endcode %}

What this test verifies:

* Two orders are provided as input: one Shipped (order 1) and one Cancelled (order 2).
* The expected output contains exactly one row for customer 10, with `total_orders: 1` and `total_revenue: 20.00`.
* The Cancelled order's items (quantity 5, value 50.00) do not appear in the output.
* `partial: true` means the test only checks the listed columns. Columns present in the output but not in `rows` are ignored.

Run this test:

```bash
vulcan test tests/test_fct_daily_sales.yaml
```

***

## Example 2: Verifying the RFM Champion segment

This test verifies that a high-value customer gets the Champions segment when their recency, frequency, and monetary scores are all 5.

`tests/test_rfm_customer_segmentation.yaml`:

{% code overflow="wrap" %}

```yaml
test_rfm_customer_segmentation_champion:
  model: gold.rfm_customer_segmentation
  inputs:
    silver.dim_customer_profile:
      - customer_id: 10
        customer_name: "Avery Stone"
        email: "avery.stone@example.com"
        signup_date: "2025-12-01"
        region_id: 1
        region_name: North
        first_order_date: "2026-01-01"
        last_order_date: "2026-05-20"
        days_since_first_order: 150
        days_since_last_order: 10
        total_orders: 4
        total_items_purchased: 12
        total_revenue: 1200.00
        avg_order_value: 300.00
        favorite_category: Widgets
        total_shipments_received: 4
        customer_segment: High Value
  outputs:
    query:
      rows:
        - customer_id: 10
          customer_name: "Avery Stone"
          region_name: North
          recency_days: 10
          frequency_orders: 4
          monetary_value: 1200.00
          recency_score: 5
          frequency_score: 5
          monetary_score: 5
          rfm_score: "555"
          rfm_segment: Champions
      partial: true
```

{% endcode %}

What this test verifies:

* A customer with 10 days since last order (recency score 5), 4 total orders (frequency score 5), and 1200.00 total revenue (monetary score 5) gets `rfm_segment: Champions` and `rfm_score: "555"`.
* The scoring CASE expressions in `gold.rfm_customer_segmentation` are correct.

***

## Partial output assertions

Use `partial: true` to assert only on a subset of output columns. This is useful when the model produces many columns and you want to focus on the ones relevant to the test scenario.

Without `partial: true`, every column in the model output must match exactly. This is stricter but catches unexpected column changes.

***

## Testing with multiple inputs

When a model joins multiple upstream tables, provide mock rows for each one. Look at the model's `FROM` and `JOIN` clauses to identify which upstream models need mock data. In `test_fct_daily_sales.yaml`, the model joins five bronze tables, so all five are provided as inputs.

***

## Running tests

```bash
# Run all tests
vulcan test

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

# Run tests for a specific model
vulcan test --select silver.fct_daily_sales
```

A passing test shows:

```
test_fct_daily_sales_excludes_cancelled_orders PASS
```

A failing test shows a diff between expected and actual output. Fix the model logic or the test expectations, then re-run.

***

## Common rules

* **No `description` field**: it is not supported in test YAML. Remove it if present.
* **`outputs` always uses `query:` with nested `rows:`**: do not put SQL strings inline under `outputs`.
* **Mock the direct dependency**: provide mock rows for the model directly referenced in your SQL's `FROM` clause, not the source tables upstream of that model.
* **`INCREMENTAL_BY_TIME_RANGE` models**: if you later switch a model to that kind, the test must include `vars.execution_time` within the mock data's date range. For the full reference, see [Tests](/concepts/resources/vulcan/components/tests.md) in the Vulcan under Concepts.


---

# 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/stage-2-productize/define-the-contract/tests.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.
