# Running tests

> 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.

<!-- id: testing-running-tests · area: testing · stability: stable · html: https://osysharp.com/reference/testing/running-tests/ -->

## Summary        {#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      {#signature}

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

## Description    {#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   {#source}

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   {#development-only}

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   {#filtering}

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   {#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}

`--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.

```json
{"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.

A test that fails or errors says where: `failureSpan` is the file, line and column of the statement that went wrong,
and `message` is the whole sentence. `osy test` is a **builder's** surface, so a security denial says everything: the
plain sentence the app's own users would see, and then the entity, the verb, the rules it was judged by and the
caller. (A real end user gets only the first half — see [what a refused user is told](https://osysharp.com/reference/security/denial-messages/).) A denial judged at commit is
reported at the statement that **wrote** the refused row, which may be a function in another file — read `file`
before `line`. The console run prints the same location as `file:line:col`.

```json
{"event":"errored","data":{"testId":"tests/invitations.test.osy::TwoPeople::a_non_member_cannot_invite","durationMs":412,"message":"You do not have permission to create this invitation. — Create of 'Invitation' denied — its create rules are: `allow create where Membership.Any(m => (m.Org == Org) && (m.Member == user))`; the row being written did not satisfy any of them (caller: mia@invite.test).","trace":null,"failureSpan":{"file":"model/invitations.osy","line":22,"column":3,"length":48}}}
```

## Examples       {#examples}

Run everything in the current project:

```console
osyrin app test
```

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

```console
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:

```osy test app=testing-running-tests
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       {#see-also}
- [[Test] / [TestFixture]](https://osysharp.com/reference/testing/test/) — `[Test]`, `[TestFixture]` and `[Skip]`: what marks a function a test, seeds a fixture, or parks a test
- [Assert](https://osysharp.com/reference/testing/assert/) — the assertions a `[Test]` body calls
- [[runas(Name)] test attribute and principal selectors](https://osysharp.com/reference/testing/runas-attribute/) — running a test as a named principal
