Tutorials

Your first golden test on DuckDB

A preview of writing fixtures and an expected output for a dedupe pipeline, running them on DuckDB, and fixing a red test.

Preview of a planned workflow

Walkthrough

Level
Beginner
Time once available
20 minutes
Steps
6
Topic
Testing

Golden tests are how Emolysis proves a pipeline before it deploys. This tutorial walks through one by hand, so the idea is concrete before you rely on the agent to write them. The golden tests docs give the full picture.

What you need

  • The Emolysis desktop app or the emo command line.
  • A project created with emo init customers.
  • An Emolysis account, signed in once in the app or with emo login; the Free plan is one.
  • No platform. Everything here runs on DuckDB on your machine.

Step 1: Describe the pipeline

Ask the agent: "Deduplicate the customers table to one row per customer id, keeping the most recently updated row." The agent picks the Dedupe / conform shape and shows the slots it filled:

  • Key columns: customer_id.
  • Ordering columns for "latest wins": updated_at, then a tie-breaker such as source_row_id.
  • Type map: customer_id as integer, updated_at as timestamp.

The tie-breaker matters. Without it, two rows with the same updated_at could swap between runs, and the output would not be deterministic.

Step 2: Look at the fixtures

Emolysis adds a test case under tests/ with two parts:

  • Input: a small table with the hard cases for this shape. For example, customer 1 appears three times with different updated_at values; customer 2 appears twice with the same updated_at; customer 3 has customer_id arriving as text in one row.
  • Expected output: exactly one row per customer: the latest row for customer 1, the row with the higher tie-breaker for customer 2, and customer 3 cast to an integer.

Fixtures are usually small Parquet files. You can open them in the app as tables.

Step 3: Run the test

Run the tests from the app, or with the command line, for example emo run template.test --params '{"pipeline": "pipelines/customers_dedupe"}'. The parameter names here are illustrative.

The test runs the pipeline on DuckDB against the input and compares the result with the expected output, row for row. It should pass in seconds.

Step 4: Break it on purpose

Edit the pipeline's slots and remove the tie-breaker, leaving only updated_at. Run the tests again. The result for customer 2 now depends on the run, and when it differs from the expected row, the test fails with the differing rows shown side by side.

In normal use, this is where the agent takes over: a red test sends it back to the template fill. It reads the failure, restores a deterministic ordering, and runs the tests again. You see each attempt.

Step 5: Add your own case

Golden tests get better with the cases that hurt you before. Add a row to the input that once broke a real pipeline, for example a customer id with surrounding spaces, and add the row you expect to the output. Run the tests. If the template does not handle it, ask the agent to extend the conform rules and run them again until green.

Step 6: Keep fixtures synthetic

Fixtures are committed to your repository and run in CI, so everyone with access can read them. Use invented values. If you want to see the pipeline on real data, pull a sample into the project's local lakehouse instead; samples are for looking, fixtures are for proving.

What you learned

  • A golden test is a fixed input, an expected output and an exact comparison.
  • Good fixtures are built around the shape's hard cases, not the happy path.
  • DuckDB makes the tests fast enough to run on every save.
  • Red goes back to the template; only green can deploy.

For the reasoning behind this loop, read the post tests before production, or the answer to what a golden test is.