Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Testing

Running tests

osyrin app test [path] [--filter <text>] [--test <id>] [--json]

Runs your app's tests against a throwaway copy of its database, reporting each test as it finishes. Your local source is what runs, so you never have to deploy to test a change.

stable1 example compiled by CItestingauthoringcli

Summary#

Runs your app's tests against a throwaway copy of its database, reporting each test as it finishes. Your local source is what runs, so you never have to deploy to test a change.

Signature#

osyrin app test [path] [--filter <text>] [--test <id>] [--json]

Description#

A test run never touches your application's real data. Before anything executes, the platform makes a private copy of your app's database, compiles your local source and your tests into that copy, and throws the whole thing away when the run ends. Nothing a test writes can outlive it.

Inside that copy the structure mirrors how you wrote your tests. Each [TestFixture] seeds its own branch once. Every [Test] that names a fixture then gets its own private clone of that seeded branch, runs there, and drops it. Two tests under the same fixture therefore never see each other's writes, no matter what order they run in — and because nothing is shared, they run at the same time.

Results stream back one at a time, as each test finishes, rather than arriving in a batch at the end. A long suite turns green in front of you.

What runs is what you wrote#

The source on your disk travels with your tests. The run compiles them together, so a test always exercises the code you are looking at — not whatever version happens to be deployed. There is no build-then-test cycle, and no way to be fooled by a stale deployment.

Tests only run against a Development app#

An application in Production mode refuses to run tests, and says so. This is deliberate and cannot be turned off per-run.

The reason is that a run copies the application's data in order to test against it, and then executes your test code with access to that copy. For an application holding real user data, that is not something to do casually. Switch the application to Development mode, or run your tests against a local platform.

Choosing what to run#

By default every test runs. Narrow it two ways:

  • --filter <text> runs the tests whose names contain text.
  • --test <id> runs exactly one test, named by its id (file::fixture::name). Repeat the flag for several.

A fixture is never filtered away. If a test survives your filter, the fixture it depends on still seeds — otherwise the test could not run at all.

A test marked [Skip("reason")] is always reported, and never runs. That is the point of marking it: the skipped test stays visible as a reminder that something is unfinished, instead of quietly disappearing.

In the editor#

The VS Code extension shows the same tests in its Test Explorer, grouped by file and fixture, updating as you type — even while a file is mid-edit and does not yet parse. Run one test, one fixture, one file, or everything. A failing assertion puts a marker on the assertion itself.

Scripting a run#

--json writes one JSON object per line, in the order events happen, so a script can react to each test as it lands. The command exits non-zero when any test fails or errors. Diagnostics go to standard error, leaving standard output as a clean stream of events.

{"event":"enqueued","data":{"testId":"tests/orders.test.osy::Seeded::Totals_Add_Up","name":"Totals_Add_Up"}}
{"event":"passed","data":{"testId":"tests/orders.test.osy::Seeded::Totals_Add_Up","durationMs":31}}
{"event":"done","data":{"passed":1,"failed":0,"errored":0,"skipped":0}}

Each testId is stable across edits: it is built from the file, the fixture, and the test's name, never from a line number. Rearrange a file and the ids stay put.

Examples#

Run everything in the current project:

osyrin app test

Run one test by name, then exactly one by id:

osyrin app test --filter Totals
osyrin app test --test "tests/orders.test.osy::Seeded::Totals_Add_Up"

A fixture and the tests that fork from it:

entity Customer {
  [Required] string Name;
}

entity Order {
  Customer Customer;
  decimal Total;
}

[TestFixture]
void Seeded() {
  var customer = new Customer { Name = "Ada" };
  new Order { Customer = customer, Total = 100 };
}

[Test(Seeded)]
void Totals_Add_Up() {
  Assert.Equal(100, Order.First().Total);
}

[Test(Seeded)]
void A_New_Order_Is_Not_Seen_By_Siblings() {
  new Order { Total = 5 };
  Assert.Equal(2, Order.Count());   // the seeded one plus this one — and no one else's
}

See also#

Related

Running tests locally

Runs your app's tests against a Platform on your own machine — no account, no network, no setup beyond a running local…