Skip to main content

Upgrading from legacy test steps

It isn't hard to upgrade from legacy TestStep to DepotTest-based depot-test testing.

The end state of this upgrade is the modern DepotTest.in(…).….run() form. Along the way the deprecated @stage-tech/depot-test/jest matchers entry is dropped — the .run() form needs no custom matcher and works under any standard test runner (Jest, Vitest, Node test).

Starting point

We'll start with the following test script (uses the deprecated @stage-tech/depot-test/jest matchers entry):

example-legacy.test.ts
// Deprecated matchers entry — the upgrade below removes the need for this import.
import "@stage-tech/depot-test/jest";
import { TestStep } from "@stage-tech/depot-test";

import * as pkg from "..";

//collect all schemas used in package
const namespace = JSON.stringify(pkg.mergedSchemas, null, 2);

describe("some clever test name", () => {
it("some clever scenario name", () => {
const step: TestStep = {
name: "contact identities view is populated",
operation: Operation.UPSERT,
dataUri: "test/clever/name/data",
expectedUri: "test/clever/name/expected",
assertions: ["some.qualified.schema.name"]
};
const steps: Array<TestStep> = [step];
expect(steps).toPassSchemaTest(namespace);
});
});

Step 1: wrap the TestStep under legacyStep()

Change the expect(…) line to use DepotTest instead and call .run() directly:

example-from-legacy-01M.test.ts
import { DepotTest, TestStep } from "@stage-tech/depot-test";

import * as pkg from "..";

//collect all schemas used in package
const namespace = JSON.stringify(pkg.mergedSchemas, null, 2);

describe("some clever test name", () => {
it("some clever scenario name", async () => {
const step: TestStep = {
name: "contact identities view is populated",
operation: Operation.UPSERT,
dataUri: "test/clever/name/data",
expectedUri: "test/clever/name/expected",
assertions: ["some.qualified.schema.name"]
};
const steps = [step];
await DepotTest.in(namespace).legacySteps(...steps).run();
});
});

Step 2: inline the parameters to legacySteps()

The entry point of DepotTest is the DepotTest.in call; this is what you should see first:

example-legacy-02M.test.ts
import { DepotTest } from "@stage-tech/depot-test";

import * as pkg from "..";

//collect all schemas used in package
const namespace = JSON.stringify(pkg.mergedSchemas, null, 2);

describe("some clever test name", () => {
it("some clever scenario name", async () => {
await DepotTest.in(namespace)
.legacySteps({
name: "contact identities view is populated",
operation: Operation.UPSERT,
dataUri: "test/clever/name/data",
expectedUri: "test/clever/name/expected",
assertions: ["some.qualified.schema.name"]
})
.run();
});
});

Step 3: untangle steps

within each legacy TestStep "curly bracket", untangle parameters into their own 'curlies':

  • migrateBefore goes alone before the step
  • migrateAfter goes alone after the step
  • expectedUri and assertions go into a new step after the current one
  • other properties stay where they are
example-legacy-03M.test.ts
import { DepotTest } from "@stage-tech/depot-test";

import * as pkg from "..";

//collect all schemas used in package
const namespace = JSON.stringify(pkg.mergedSchemas, null, 2);

describe("some clever test name", () => {
it("some clever scenario name", async () => {
await DepotTest.in(namespace)
.legacySteps({
operation: Operation.UPSERT,
dataUri: "test/clever/name/data",
}, {
name: "contact identities view is populated",
expectedUri: "test/clever/name/expected",
assertions: ["some.qualified.schema.name"]
})
.run();
});
});

Step 4: update seeds, migrations and checks to use explicit primitives

Extract steps that do a migration or check assertions to their own primitive (.migrateTo(…) or .check(…), respectively); consider extracting seeding steps that do an upsert to their own primitive (.setContent(…)); turn the rest as .transaction(…) instead of .legacyStep(…).

  • When migrating a transaction of type operation: Operation.UPSERT that seeds your test environment, drop the operation fields.
  • When migrating the check() action, rename assertions to actual.

::: info setContent() first truncates the targeted tables (or reified views). When you run with SNOWFLAKE_SCHEMA_PREFIX set, this will prevent debris from a previous run from interfering with your test. :::

example-legacy-04M.test.ts
import { DepotTest } from "@stage-tech/depot-test";

import * as pkg from "..";

//collect all schemas used in package
const namespace = JSON.stringify(pkg.mergedSchemas, null, 2);

describe("some clever test name", () => {
it("some clever scenario name", async () => {
await DepotTest.in(namespace)
.setContent({
dataUri: "test/clever/name/data",
})
.check({
name: "contact identities view is populated",
expectedUri: "test/clever/name/expected",
actual: ["some.qualified.schema.name"]
})
.run();
});
});
tip

.transaction() lacks some fields compared to .legacyStep(), which are better carried by the .check() and .migrateTo() calls instead. The intent is to use the TypeScript compiler to guide you towards the 5.3.0 DepotTest API.

Step 5: extract transaction actions to explicit primitives (optional)

Using the explicit primitives better conveys the intent of your test scenario, and summons assistance from the TypeScript compiler in order to get the parameters right.

Apply the following pattern transforms until you've exhausted all .legacySteps() calls:

Patternfromto
Distributivity.legacyStep(A, B, … , Z).legacyStep(A)
.legacyStep(B)
.…
.legacyStep(Z)
Migrations.legacyStep({ migrateBefore: … })
or .legacyStep({ migrateAfter: … })
.migrateTo(…)
Checks.legacyStep({ expectedUri: …, assertions: … }).check({ expectedUri: …, actual: … })
Initial set-up.legacyStep({
    operation: Operation.UPSERT,
    dataUri: 'path/to/data'
  })
.check({
    operation: Operation.UPSERT,
    dataUri: 'path/to/data'
  })
upsert (from files).legacyStep({
    operation: Operation.UPSERT,
    dataUri: 'path/to/data'
  })
.upsert({ dataUri: 'path/to/data' })
upsert (from schema).legacyStep({
    operation: Operation.UPSERT,
    source: 'my.source'
    target: 'my.target'
  })
.upsert({
    source: 'my.source',
    target: 'my.target'
   })
merge (from files).legacyStep({
    operation: Operation.MERGE,
    dataUri: 'path/to/data'
  })
.merge({ dataUri: 'path/to/data' })
merge (from schema).legacyStep({
    operation: Operation.MERGE,
    source: 'my.source'
    target: 'my.target'
  })
.merge({
    source: 'my.source',
    target: 'my.target'
   })
insert (from files).legacyStep({
    operation: Operation.INSERT_IGNORE,
    dataUri: 'path/to/data'
  })
.insert({
    dataUri: 'path/to/data',
    ignoreConflicts: true
  })
insert (from schema).legacyStep({
    operation: Operation.MERGE_IGNORE,
    source: 'my.source'
    target: 'my.target'
  })
.insert({
    source: 'my.source',
    target: 'my.target'
    ignoreConflicts: true
   })

(etc.) You can preserve the name properties where they are important, but otherwise DepotTest will generate descriptive step names for you.

tip

This step is optional. A reason not to apply it is to keep the shape of transactions identical between the tested code (here) and transactions invoked in the 'real' code via the Transactions API step function.

example-legacy-05M.test.ts
import { DepotTest } from "@stage-tech/depot-test";

import * as pkg from "..";

//collect all schemas used in package
const namespace = JSON.stringify(pkg.mergedSchemas, null, 2);

describe("some clever test name", () => {
it("some clever scenario name", async () => {
await DepotTest.in(namespace)
.setContent("test/clever/name/data")
.check({
name: "contact identities view is populated",
expectedUri: "test/clever/name/expected",
actual: ["some.qualified.schema.name"]
})
.run();
});
});

Step 6 (miscellaneous): remove unnecessary noise

DepotTest.in(…) accepts a JSON string or Buffer or a path to a JSON file, or the MergedSchemas. Skip unnecessary conversions, use the most straightforward structure you have at hand:

example-legacy-06M.test.ts
import { DepotTest } from "@stage-tech/depot-test";

import * as pkg from "..";

describe("some clever test name", () => {
it("some clever scenario name", async () => {
await DepotTest.in(pkg.mergedSchemas)
.setContent("test/clever/name/data")
.check({
name: "contact identities view is populated",
expectedUri: "test/clever/name/expected",
actual: ["some.qualified.schema.name"]
})
.run();
});
});

Step 7

Congratulations!