# 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 platform. `osyrin dev` starts that platform; `osy test` runs your tests against it.

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

## Summary        {#summary}

Runs your app's tests against a Platform running on your own machine — no account, no network, no cloud. It is the
local counterpart of [Running tests](https://osysharp.com/reference/testing/running-tests/): the same tests, the same throwaway-copy isolation, the same streamed
report. The only difference is where it runs.

## Signature      {#signature}

```osy syntax
osy test [path] [--filter <text>] [--test <id>] [--pixels] [--headed] [--json]
```

## Description    {#description}

### Start a local platform, then test against it   {#starting}

A local platform is a full Platform that runs on your machine, with a database it manages itself. Start one from your
project directory:

```console
osyrin dev
```

The first start downloads a small database bundle once; after that it is up in seconds. It binds to your machine only —
nothing outside can reach it — and it needs no account and no cloud project. Leave it running in a terminal.

In another terminal, run your tests against it:

```console
osy test
```

That is the whole loop: edit your source, `osy test`, watch it turn green. There is nothing to log into and nothing to
deploy — `osy test` finds the running local platform for your project on its own, ensures your app exists there,
compiles the source on your disk into it, and runs your tests.

### `--pixels` — the same tests, in a real browser   {#pixels}

`osy test` renders your pages in a headless DOM with no font engine and no compositor. That is what makes it fast
enough to run on every change, and it means the run is **blind to layout**: a control is "visible" whether it is on
its card, off it, or underneath something else.

`osy test --pixels` runs the **same tests** in a real browser instead. Nothing about your test file changes — the
same locators, the same refusals — but the geometric claims in [Layout assertions — is it actually usable on screen?](https://osysharp.com/reference/testing/ui-layout/) are actually judged, and
`Ui.Shot("label")` writes a PNG you can open.

```console
osy test                     # behaviour, in seconds
osy test --pixels            # the same tests, with layout checked and screenshots written
osy test --pixels --headed   # …and show the browser, slowed down, so you can watch it drive
```

It is opt-in because it costs a browser: the tier needs Playwright and a Chromium, or the Chrome already on your
machine, which it prefers. The check runs **before anything is compiled or booted**, so a machine that cannot run it
is told in a second — with the command that fixes it — rather than a minute into a run.

### The tests run exactly as they would anywhere   {#parity}

Local is not a weaker mode. Your app's own security is enforced just as it is in production: a `[Test]` runs as an
anonymous, secured caller, so a test that creates or reads data needs your model to grant it — a plain new app is
secure by default. This is the same behavior described in [Running tests](https://osysharp.com/reference/testing/running-tests/); the point of running locally is
speed and privacy, never a relaxed rulebook. The starter model a new project ships with grants exactly what its first
test needs and nothing more.

Everything else is identical to a remote run: each `[TestFixture]` seeds its own private branch, each `[Test]` forks
its own throwaway clone, results stream back one at a time, and nothing a test writes survives it. See
[Running tests](https://osysharp.com/reference/testing/running-tests/) for the full model.

### Choosing what to run   {#filtering}

The same two ways as a remote run:

- `--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, and a `[Skip("reason")]` test is always reported and never runs.

### 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, and diagnostics go to standard error — standard output stays
a clean stream of events. The event shape is the same as a remote run.

### When there is no local platform   {#no-platform}

If no local platform is running for your project, `osy test` says so and stops, rather than silently reaching
elsewhere:

```console
No local platform is running for this project. Start one with `osyrin dev`.
```

Run `osyrin dev` and try again.

## Examples       {#examples}

The two-terminal loop — one platform, many test runs:

```console
# terminal 1
osyrin dev

# terminal 2
osy test
osy test --filter Totals
osy test --test "tests/orders.test.osy::Seeded::Totals_Add_Up"
```

A first test in a freshly scaffolded project, which passes because the starter model grants it:

```osy title="the model a fresh project scaffolds" test app=testing-running-tests-locally
// model/note.osy
entity Note {
  [Required] string Title;
  security { allow read, create when IsAuthenticated || IsAnonymous; }
}
```

```osy title="the first test that passes against it" test app=testing-running-tests-locally
// tests/note.test.osy
[Test]
void a_new_note_keeps_its_title() {
  var note = new Note { Title = "First note" };
  Assert.Equal("First note", note.Title);
}
```

## See also       {#see-also}

[Running tests](https://osysharp.com/reference/testing/running-tests/) — the same run against a remote platform, and the full description of fixtures, isolation,
and the streamed report.

[Debugging tests locally](https://osysharp.com/reference/testing/debugging-tests-locally/) — debug a single test locally with breakpoints and stepping in your editor.

[Running a local platform](https://osysharp.com/reference/local/running-a-local-platform/) — the local platform these tests run against, and how to start it.
