# [Test] / [TestFixture]

> A test is an ordinary function marked [Test]. It runs against a throwaway clone of the app, so it may create rows and break rules freely. A [TestFixture] seeds the data once and every test forks from it.

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

## Summary        {#summary}
A test is an ordinary function marked `[Test]`. It runs against a **throwaway clone** of the app — its own database,
made for it and thrown away after — so it can create rows, violate rules and assert on the wreckage without touching
anything real, and without cleaning up after itself.

A `[TestFixture]` builds the starting data **once**; every test that names it forks from that state.

## Signature      {#signature}
```osy syntax
[TestFixture]
void <Seed>() { … }               // build the starting data, once

[Test]
void <Name>() { … }               // a test with no fixture

[Test(<Seed>)]
void <Name>() { … }               // a test that starts from <Seed>'s data
```

## Description    {#description}

### A test is a function   {#a-function}
There is no separate test language. A test is a function: it can call your functions, create rows, run queries — the
whole model is in scope.

```osy title="a model" test app=testing-test
entity Order {
  [Required] string Code;
  decimal Total;

  security { allow create, read when IsAuthenticated || IsAnonymous; }   // an entity with no security block is denied to everyone
}

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

```osy title="…and a test of it" run app=testing-test
[Test]
void Placing_an_order_stores_its_total() {
  PlaceOrder("A1", 42m);
  Assert.Equal(42m, Order.Single(o => o.Code == "A1").Total);
}
```

#### A row a function hands back is a live row of the test's unit of work   {#returned-rows}
A function runs in a child scope of its caller's unit of work, committed upward when it returns. The row it returns
— created there, or merely loaded there — is **the same live row** the test would have got from a query: assign to
it and the write is staged in the test's unit of work, and the next `Assert.*` flushes it like any other. Nothing to
re-read, nothing to re-attach.

```osy title="a function that returns what it made" test app=testing-test-returned
entity Organization {
  [Required, MaxLength(100)] string Name;
  [Required, Unique, MaxLength(60)] string Slug;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

Organization Found(string name, string slug) {
  var org = new Organization { Name = name, Slug = slug };
  UnitOfWork.Commit();      // with or without this — a merely staged return value is tracked the same way
  return org;
}
```

```osy title="…and the row it returned, written to" run app=testing-test-returned
[Test]
void A_returned_row_is_still_the_tests_row() {
  var acme = Found("Acme", "acme");
  acme.Name = "Acme Travel";                                          // a staged write in THIS test's unit of work
  Assert.Equal("Acme Travel", Organization.Single(o => o.Slug == "acme").Name);   // read back through a fresh query
}
```

The one row that is **not** live is one created in a scope that then faulted — a `try` body or a call that threw
after making it. That row never existed, and reading or writing it is refused by name: *the Organization row … was
created in a scope that has since been discarded*. Create it where it is wanted, or catch the fault inside the scope
that created it.

**The same is true after an `Assert.Throws` or `Assert.Denied` catches a commit fault.** The refusal that assert was
written to prove is a failed transaction, so everything the test had staged and not committed is thrown away with
it — otherwise the same violation trips again at the next assert and errors the test *after* it has already passed.
A row the test **created** before that point is therefore gone too, and touching it afterwards says so: *the
Organization row … was created and then DISCARDED*. Seed through a [[#fixture|`[TestFixture]`]], or commit what you
want to keep, before the assert that expects a fault.

### A fixture seeds once; tests fork from it   {#fixture}
Building the same three customers at the top of nine tests is slow and, worse, it is nine places to change. A
`[TestFixture]` builds them once:

```osy title="a fixture, and two tests that fork from it" run app=testing-test
[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());     // this test's own clone: the seeded two, plus this one
}
```

The second test creates a third order and sees three. The first test still sees two. **Tests never see each other's
writes** — each forks its own copy of the fixture's data, so they can run in any order, or at the same time, and
neither has to undo anything.

That isolation is the whole point: a test suite where one test's leftovers change another's outcome is a suite that
fails at random and gets ignored.

### The fixture is unsecured; the test body is not   {#security}
They do not run under the same rules, and this catches everyone once:

- a **`[TestFixture]`** seeds **unsecured** — it can create rows across every entity, including ones nobody is allowed
  to create, so building a scenario never means weakening a rule;
- a **`[Test]` body** runs **secured, as an initially-anonymous principal** — your rules are on, and nobody is signed
  in.

So a test that simply calls a function may be **denied**, and that is the system working. To act as a real user, name
a principal and run as them ([[runas(Name)] test attribute and principal selectors](https://osysharp.com/reference/testing/runas-attribute/)). The whole model is in [the testing guide](https://osysharp.com/reference/testing/index/).

### What should a test be called?   {#naming}
A test's name is read by a person deciding whether the failure matters. `A_new_order_is_not_seen_by_its_siblings`
tells them; `Test3` does not.

### Parking a test with `[Skip]`   {#skip}
A test that cannot run yet — it depends on a capability the platform doesn't offer, or an intended behaviour that
isn't built — is not deleted. Marking the `[Test]` with **`[Skip("reason")]`** keeps it in the suite as the executable
record of the intended behaviour: it is still **discovered** and listed, but never **run**, and every surface renders
it as a skip. The reason string is **required** — it is the visible note of *why* the test is parked:

```osy title="a parked test" test app=testing-test
[Test]
[Skip("KNOWN-GAP: refunds aren't implemented yet")]
void A_refund_restores_stock() {
  // The intended behaviour, written out — it compiles, so it can't rot, and it turns on
  // the day the gap closes and the [Skip] comes off.
  Assert.Equal(0, Order.Count());
}
```

`[Skip]` applies only to a `[Test]` (a `[TestFixture]` cannot be skipped), and its body must still **compile** — the
whole value of a parked test is that it is a real, type-checked spec, not a comment.

## See also       {#see-also}
- [Testing (real app, real data, real rules)](https://osysharp.com/reference/testing/index/) — the testing guide: isolation, who a test acts as, and how you prove a rule
- [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/) — running a test as a particular user, to test security
- [Running tests locally](https://osysharp.com/reference/testing/running-tests-locally/) — `osy test`
