# Testing (real app, real data, real rules)

> A test in Osy# is not a unit test with the world mocked out. It is your app, running against its own throwaway database, with your security rules switched on. That is what makes it worth writing — and it is why the one thing you must understand is who a test is ACTING AS: a test body runs secured, as an anonymous stranger, until you say otherwise.

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

## Summary        {#summary}
A test is an ordinary function marked `[Test]`. It calls your real functions, against your real entities, with your
real security rules enforced — on **its own private copy of the database**, thrown away when it finishes.

There is nothing to mock, because there is nothing in the way:

```osy title="the model" test app=testing-index
entity Order {
  [Required, Unique, MaxLength(20)] string Code;
  decimal Total;
  invariant Total >= 0;

  security { allow create, read, update when IsAuthenticated || IsAnonymous; }
}

void PlaceOrder(string code, decimal total) {
  var order = new Order { Code = code, Total = total };
}
```

```osy title="…and a test of it" run app=testing-index
[Test]
void Placing_an_order_stores_its_total() {
  PlaceOrder("A1", 42m);

  Assert.Equal(42m, Order.Single(o => o.Code == "A1").Total);
}
```

No repository to stub, no in-memory database to configure, no test double for your own model. You wrote the function;
the test calls it.

## Description    {#description}

### What a test actually runs against   {#isolation}
Before anything executes, the platform copies your app's database, compiles **your local source** and your tests into
that copy, and throws it away at the end. Nothing a test writes can outlive it, and nothing you have deployed can
mislead you — what runs is what is on your disk.

Inside that copy, the structure mirrors how you wrote the tests:

- a **`[TestFixture]`** builds the starting data **once**;
- **every `[Test]` that names it forks its own private clone** of that seeded state.

So two tests under the same fixture never see each other's writes, in any order, at the same time. That isolation is
the whole point: a suite where one test's leftovers change another's outcome is a suite that fails at random and then
gets ignored.

```osy title="a fixture seeds once; each test forks from it" run app=testing-index
[TestFixture]
void Seeded() {
  PlaceOrder("A1", 100m);
  PlaceOrder("A2", 50m);
}

[Test(Seeded)]
void The_seeded_orders_are_there() {
  Assert.Equal(2, Order.Count());
}

[Test(Seeded)]
void A_new_order_is_not_seen_by_its_siblings() {
  PlaceOrder("A3", 5m);

  Assert.Equal(3, Order.Count());   // its OWN clone: the seeded two, plus this one
}
```

The second test creates a third order and sees three. The first still sees two. Neither undoes anything.

### The one thing to understand: who is the test acting as?   {#acting-as}
**This is the paragraph that will save you an afternoon.** The fixture and the test body do not run under the same
rules, and the difference is deliberate:

| | Security | Why |
|---|---|---|
| **`[TestFixture]`** | **UNSECURED** | it is scaffolding. It seeds freely across every entity, including ones nobody is allowed to create, so that setting up a scenario never requires weakening a rule. |
| **`[Test]` body** | **SECURED, as an initially-anonymous principal** | it is the thing under test. Your rules are switched on, and nobody is signed in — so a denial is a *real* denial. |

Two consequences, and they explain almost every surprise a newcomer hits:

**1. A test that just calls a function may be denied — and that is the system working.** An app is
[deny-all by default](https://osysharp.com/reference/security/secure-by-default/), and a `[Test]` is a stranger:

```osy title="the model — an app with users, and a table only they may write" test app=testing-index-secured
[Principal]
entity User {
  [Required, MaxLength(60)] string Name;
  security { allow read where Id == user.Id; }
}

entity Ledger {
  [Required, MaxLength(80)] string Entry;
  security { allow create, read when IsAuthenticated; }
}

void Record(string entry) {
  var line = new Ledger { Entry = entry };   // no auth code — the rule does the work
}
```

```osy title="a stranger is refused — which is exactly what you want to be able to prove" run app=testing-index-secured
[Test]
void An_anonymous_caller_cannot_write_to_the_ledger() {
  Assert.Denied(() => Record("payroll"));

  Assert.Empty(Ledger.ToList());
}
```

**2. To test what a real user does, you must ACT AS one.** That is what [[testing-runas-attribute|`[runas]`]] is for,
and it is the only way security gets tested at all.

### Testing the rules — the part nobody else can do for you   {#security}
A security rule is the one kind of code whose bugs are invisible until they are catastrophic. A rule that is too
*strict* fails loudly the first time someone uses the app. A rule that is too *loose* fails silently, forever.

So: declare a **`principal`** — a name for a seeded `[Principal]` row — and run the test as them.

```osy title="the model, and its rules" test app=testing-index-rules
[Principal]
entity User {
  [Required, MaxLength(60)] string Name;
  security { allow read where Id == user.Id; }
}

entity Doc {
  [Required] User Owner;
  [MaxLength(200)] string Title;
  security { allow read where Owner == user; }   // you see your own documents. Nobody else's.
}
```

```osy title="…and the proof that they hold" run app=testing-index-rules
principal Alice => User.Single(u => u.Name == "Alice");
principal Bob   => User.Single(u => u.Name == "Bob");

[TestFixture]
void Seed() {
  // The fixture is UNSECURED, so it can seed rows that nobody is allowed to create.
  var alice = new User { Name = "Alice" };
  var bob   = new User { Name = "Bob" };
  var a = new Doc { Owner = alice, Title = "alice-doc" };
  var b = new Doc { Owner = bob,   Title = "bob-doc" };
}

[Test(Seed)]
[runas(Alice)]
void Alice_sees_her_own_document() {
  Assert.Single(Doc.ToList());
  Assert.Equal("alice-doc", Doc.Single().Title);
}

[Test(Seed)]
[runas(Bob)]
void Bob_cannot_see_Alices_document() {
  // Not "is hidden in the UI" — the row is not SELECTED. The rule is inside the query.
  Assert.Null(Doc.FirstOrDefault(d => d.Title == "alice-doc"));
}

[Test(Seed)]
void A_stranger_sees_nothing_at_all() {
  Assert.Empty(Doc.ToList());
}
```

Three tests, and between them they pin the rule from every side: the owner sees it, another user does not, and a
stranger sees nothing. `[runas]` **binds, never creates** — the selector must resolve to a row the fixture seeded, so
a denial test can never quietly pass against a principal production would never grant.

**A rule you have not tested is a rule you only believe you wrote.**

### What to assert   {#asserting}
The everyday assertions are equality and null checks. The two that earn their keep are the ones that prove a
*refusal*:

- **`Assert.Denied(() => …)`** — the acting principal is **refused** by a security rule. This is how you prove a rule
  bites.
- **`Assert.Throws<T>(() => …)`** — the code **faults**: your own [`throw`](https://osysharp.com/reference/function/throw/), or a broken
  [invariant](https://osysharp.com/reference/entity/invariants/) or [constraint](https://osysharp.com/reference/entity/constraints/) arriving as a `ValidationException`.

The distinction matters: `Denied` means *you were not allowed*, `Throws` means *it was not valid*. A test that
confuses them will pass for the wrong reason. See [Assert](https://osysharp.com/reference/testing/assert/) for the full set.

**Testing what the SCREEN does is [Ui — drive the app's UI from a test](https://osysharp.com/reference/testing/ui/)** — `Ui.Visit` opens a route, `Ui.Click`
presses what a person would press, and the same `Assert.*` verbs ask the questions. It is the same test, so nothing
on this page stops applying; `within:` is how you address one row when several read alike.

⛔ **And what the screen does is not the same question as whether a person can USE it.** Every assertion above is
about text or state, and all of them pass on a page that is visually broken — a button under an overlay, a label cut
off by its own box. [Layout assertions — is it actually usable on screen?](https://osysharp.com/reference/testing/ui-layout/) is the vocabulary for that, checked by `osy test --pixels` in a real
browser; under plain `osy test` those claims report themselves NOT CHECKED rather than green.

```osy title="proving the invariant is real, not decorative" run app=testing-index
[Test]
void An_order_cannot_have_a_negative_total() {
  Assert.Throws<ValidationException>(() => PlaceOrder("A9", -1m));

  Assert.Empty(Order.ToList());   // and the refused row was not left behind
}
```

### Why does an assert commit my writes?   {#asserts-settle}
Before every `Assert.*`, the platform **commits** whatever the test has written so far — which is why a test reads
real, stored rows and never needs a `UnitOfWork.Commit()` of its own.

It has one consequence worth knowing, because it will otherwise confuse you for half an hour: **an assert changes the
state the next line runs against.** If you are testing something that depends on work being *uncommitted* — how a query
treats a pending edit, say ([Querying data](https://osysharp.com/reference/query/index/)) — an assert placed before it will have settled that work, and you will
observe the committed behaviour instead.

So put the act you are testing **before** the assertions about it, and give each scenario its own `[Test]` (they fork
their own copies anyway, so this costs nothing).

### Parking a test you cannot write yet   {#skip}
`[Skip("reason")]` parks a `[Test]`, and the reason is **required**. Use it when the behaviour you want is not
expressible yet — the parked test is the executable statement of what you meant, and it surfaces as a real skip in the
run rather than vanishing. Deleting it would delete the only record that the gap exists.

### Running them   {#running}
`osy test` compiles your source and your tests together and runs them against a throwaway copy, streaming each result
as it finishes. Tests run only against a **Development** app — see [Running tests](https://osysharp.com/reference/testing/running-tests/) and
[Running tests locally](https://osysharp.com/reference/testing/running-tests-locally/), and [Debugging tests locally](https://osysharp.com/reference/testing/debugging-tests-locally/) when one is failing and you want to stop
inside it.

## See also       {#see-also}
- [[Test] / [TestFixture]](https://osysharp.com/reference/testing/test/) — `[Test]` and `[TestFixture]`
- [Ui — drive the app's UI from a test](https://osysharp.com/reference/testing/ui/) — driving the app's UI from a test: `Ui.Visit`/`Ui.Click`/`Ui.Fill`, and `within:` when two rows read alike
- [Layout assertions — is it actually usable on screen?](https://osysharp.com/reference/testing/ui-layout/) — the GEOMETRIC claims: is it clickable, inside its card, cut off? — and `osy test --pixels`
- [Assert](https://osysharp.com/reference/testing/assert/) — the assertions, including `Assert.Throws` and `Assert.Denied`
- [[runas(Name)] test attribute and principal selectors](https://osysharp.com/reference/testing/runas-attribute/) — `principal` declarations and `[runas(Name)]`
- [runas](https://osysharp.com/reference/testing/runas/) — the `runas(…) { }` block, for two principals in one test
- [Outbound calls in a test](https://osysharp.com/reference/testing/outbound-calls/) — a `[Test]` reaches the network for real, which is the only way a suite can prove the endpoint works
- [Calling your own REST API from a test](https://osysharp.com/reference/testing/api-calls/) — the OTHER direction: calling a route your own app publishes, and asserting the status it answers
- [Running tests](https://osysharp.com/reference/testing/running-tests/) — `osy test`
- [The security model](https://osysharp.com/reference/security/index/) — the rules you are proving
- [Functions (the unit of work)](https://osysharp.com/reference/function/index/) — the functions you are testing
