# Writing Tests {#sec-writing-nixos-tests}
A NixOS test is a module that has the following structure:
```nix
{
# One or more machines:
nodes =
{ machine =
{ config, pkgs, ... }: { /* ... */ };
machine2 =
{ config, pkgs, ... }: { /* ... */ };
# …
};
testScript =
''
Python code…
'';
}
```
We refer to the whole test above as a test module, whereas the values
in [`nodes.<name>`](#test-opt-nodes) are NixOS modules themselves.
The option [`testScript`](#test-opt-testScript) is a piece of Python code that executes the
test (described below). During the test, it will start one or more
virtual machines, the configuration of which is described by
the option [`nodes`](#test-opt-nodes).
An example of a single-node test is
[`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix).
It only needs a single machine to test whether users can log in
on the virtual console, whether device ownership is correctly maintained
when switching between consoles, and so on. An interesting multi-node test is
[`nfs/simple.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix).
It uses two client nodes to test correct locking across server crashes.
## Calling a test {#sec-calling-nixos-tests}
Tests are invoked differently depending on whether the test is part of NixOS or lives in a different project.
### Testing within NixOS {#sec-call-nixos-test-in-nixos}
Tests that are part of NixOS are added to [`nixos/tests/all-tests.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix).
```nix
{
hostname = runTest ./hostname.nix;
}
```
Overrides can be added by defining an anonymous module in `all-tests.nix`.
```nix
{
hostname = runTest {
imports = [ ./hostname.nix ];
defaults.networking.firewall.enable = false;
};
}
```
You can run a test with attribute name `hostname` in `nixos/tests/all-tests.nix` by invoking:
```shell
cd /my/git/clone/of/nixpkgs
nix-build -A nixosTests.hostname
```
### Testing outside the NixOS project {#sec-call-nixos-test-outside-nixos}
Outside the `nixpkgs` repository, you can use the `runNixOSTest` function from
`pkgs.testers`:
```nix
let pkgs = import <nixpkgs> {};
in
pkgs.testers.runNixOSTest {
imports = [ ./test.nix ];
defaults.services.foo.package = mypkg;
}
```
`runNixOSTest` returns a derivation that runs the test.
## Configuring the nodes {#sec-nixos-test-nodes}
There are a few special NixOS options for test VMs:
`virtualisation.memorySize`
: The memory of the VM in megabytes.
`virtualisation.vlans`
: The virtual networks to which the VM is connected. See
[`nat.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix)
for an example.
`virtualisation.writableStore`
: By default, the Nix store in the VM is not writable. If you enable
this option, a writable union file system is mounted on top of the
Nix store to make it appear writable. This is necessary for tests
that run Nix operations that modify the store.
For more options, see the module
[`qemu-vm.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix).
The test script is a sequence of Python statements that perform various
actions, such as starting VMs, executing commands in the VMs, and so on.
Each virtual machine is represented as an object stored in the variable
`name` if this is also the identifier of the machine in the declarative
config. If you specified a node `nodes.machine`, the following example starts the
machine, waits until it has finished booting, then executes a command
and checks that the output is more-or-less correct:
```py
machine.start()
machine.wait_for_unit("default.target")
t.assertIn("Linux", machine.succeed("uname"), "Wrong OS")
```
The first line is technically unnecessary; machines are implicitly started