Run formatter
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
|
||||
Each feature added to clan should be tested extensively via automated tests.
|
||||
|
||||
This document covers different methods of automated testing, including creating, running and debugging such tests.
|
||||
This document covers different methods of automated testing, including creating,
|
||||
running and debugging such tests.
|
||||
|
||||
In order to test the behavior of clan, different testing frameworks are used depending on the concern:
|
||||
In order to test the behavior of clan, different testing frameworks are used
|
||||
depending on the concern:
|
||||
|
||||
- NixOS VM tests: for high level integration
|
||||
- NixOS container tests: for high level integration
|
||||
@@ -13,37 +15,48 @@ In order to test the behavior of clan, different testing frameworks are used dep
|
||||
|
||||
## NixOS VM Tests
|
||||
|
||||
The [NixOS VM Testing Framework](https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests) is used to create high level integration tests, by running one or more VMs generated from a specified config. Commands can be executed on the booted machine(s) to verify a deployment of a service works as expected. All machines within a test are connected by a virtual network. Internet access is not available.
|
||||
The
|
||||
[NixOS VM Testing Framework](https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests)
|
||||
is used to create high level integration tests, by running one or more VMs
|
||||
generated from a specified config. Commands can be executed on the booted
|
||||
machine(s) to verify a deployment of a service works as expected. All machines
|
||||
within a test are connected by a virtual network. Internet access is not
|
||||
available.
|
||||
|
||||
### When to use VM tests
|
||||
|
||||
- testing that a service defined through a clan module works as expected after deployment
|
||||
- testing that a service defined through a clan module works as expected after
|
||||
deployment
|
||||
- testing clan-cli subcommands which require accessing a remote machine
|
||||
|
||||
### When not to use VM tests
|
||||
|
||||
NixOS VM Tests are slow and expensive. They should only be used for testing high level integration of components.
|
||||
VM tests should be avoided wherever it is possible to implement a cheaper unit test instead.
|
||||
NixOS VM Tests are slow and expensive. They should only be used for testing high
|
||||
level integration of components. VM tests should be avoided wherever it is
|
||||
possible to implement a cheaper unit test instead.
|
||||
|
||||
- testing detailed behavior of a certain clan-cli command -> use unit testing via pytest instead
|
||||
- testing detailed behavior of a certain clan-cli command -> use unit testing
|
||||
via pytest instead
|
||||
- regression testing -> add a unit test
|
||||
|
||||
### Finding examples for VM tests
|
||||
|
||||
Existing nixos vm tests in clan-core can be found by using ripgrep:
|
||||
|
||||
```shellSession
|
||||
rg self.clanLib.test.baseTest
|
||||
```
|
||||
|
||||
### Locating definitions of failing VM tests
|
||||
|
||||
All nixos vm tests in clan are exported as individual flake outputs under `checks.x86_64-linux.{test-attr-name}`.
|
||||
If a test fails in CI:
|
||||
All nixos vm tests in clan are exported as individual flake outputs under
|
||||
`checks.x86_64-linux.{test-attr-name}`. If a test fails in CI:
|
||||
|
||||
- look for the job name of the test near the top if the CI Job page, like, for example `gitea:clan/clan-core#checks.x86_64-linux.borgbackup/1242`
|
||||
- in this case `checks.x86_64-linux.borgbackup` is the attribute path
|
||||
- note the last element of that attribute path, in this case `borgbackup`
|
||||
- search for the attribute name inside the `/checks` directory via ripgrep
|
||||
- look for the job name of the test near the top if the CI Job page, like, for
|
||||
example `gitea:clan/clan-core#checks.x86_64-linux.borgbackup/1242`
|
||||
- in this case `checks.x86_64-linux.borgbackup` is the attribute path
|
||||
- note the last element of that attribute path, in this case `borgbackup`
|
||||
- search for the attribute name inside the `/checks` directory via ripgrep
|
||||
|
||||
example: locating the vm test named `borgbackup`:
|
||||
|
||||
@@ -57,14 +70,15 @@ $ rg "borgbackup =" ./checks
|
||||
|
||||
### Adding vm tests
|
||||
|
||||
Create a nixos test module under `/checks/{name}/default.nix` and import it in `/checks/flake-module.nix`.
|
||||
|
||||
Create a nixos test module under `/checks/{name}/default.nix` and import it in
|
||||
`/checks/flake-module.nix`.
|
||||
|
||||
### Running VM tests
|
||||
|
||||
```shellSession
|
||||
nix build .#checks.x86_64-linux.{test-attr-name}
|
||||
```
|
||||
|
||||
(replace `{test-attr-name}` with the name of the test)
|
||||
|
||||
### Debugging VM tests
|
||||
@@ -73,12 +87,14 @@ The following techniques can be used to debug a VM test:
|
||||
|
||||
#### Print Statements
|
||||
|
||||
Locate the definition (see above) and add print statements, like, for example `print(client.succeed("systemctl --failed"))`, then re-run the test via `nix build` (see above)
|
||||
Locate the definition (see above) and add print statements, like, for example
|
||||
`print(client.succeed("systemctl --failed"))`, then re-run the test via
|
||||
`nix build` (see above)
|
||||
|
||||
#### Interactive Shell
|
||||
|
||||
- Execute the vm test outside the nix Sandbox via the following command:
|
||||
`nix run .#checks.x86_64-linux.{test-attr-name}.driver -- --interactive`
|
||||
`nix run .#checks.x86_64-linux.{test-attr-name}.driver -- --interactive`
|
||||
- Then run the commands in the machines manually, like for example:
|
||||
```python3
|
||||
start_all()
|
||||
@@ -87,19 +103,22 @@ Locate the definition (see above) and add print statements, like, for example `p
|
||||
|
||||
#### Breakpoints
|
||||
|
||||
To get an interactive shell at a specific line in the VM test script, add a `breakpoint()` call before the line to debug, then run the test outside of the sandbox via:
|
||||
`nix run .#checks.x86_64-linux.{test-attr-name}.driver`
|
||||
|
||||
To get an interactive shell at a specific line in the VM test script, add a
|
||||
`breakpoint()` call before the line to debug, then run the test outside of the
|
||||
sandbox via: `nix run .#checks.x86_64-linux.{test-attr-name}.driver`
|
||||
|
||||
## NixOS Container Tests
|
||||
|
||||
Those are very similar to NixOS VM tests, as in they run virtualized nixos machines, but instead of using VMs, they use containers which are much cheaper to launch.
|
||||
As of now the container test driver is a downstream development in clan-core.
|
||||
Basically everything stated under the NixOS VM tests sections applies here, except some limitations.
|
||||
Those are very similar to NixOS VM tests, as in they run virtualized nixos
|
||||
machines, but instead of using VMs, they use containers which are much cheaper
|
||||
to launch. As of now the container test driver is a downstream development in
|
||||
clan-core. Basically everything stated under the NixOS VM tests sections applies
|
||||
here, except some limitations.
|
||||
|
||||
Limitations:
|
||||
|
||||
- Cannot run in interactive mode, however while the container test runs, it logs a nsenter command that can be used to log into each of the container.
|
||||
- Cannot run in interactive mode, however while the container test runs, it logs
|
||||
a nsenter command that can be used to log into each of the container.
|
||||
- setuid binaries don't work
|
||||
|
||||
### Where to find examples for NixOS container tests
|
||||
@@ -110,10 +129,10 @@ Existing NixOS container tests in clan-core can be found by using `ripgrep`:
|
||||
rg self.clanLib.test.containerTest
|
||||
```
|
||||
|
||||
|
||||
## Python tests via pytest
|
||||
|
||||
Since the Clan CLI is written in python, the `pytest` framework is used to define unit tests and integration tests via python
|
||||
Since the Clan CLI is written in python, the `pytest` framework is used to
|
||||
define unit tests and integration tests via python
|
||||
|
||||
Due to superior efficiency,
|
||||
|
||||
@@ -121,43 +140,52 @@ Due to superior efficiency,
|
||||
|
||||
- writing unit tests for python functions and modules, or bugfixes of such
|
||||
- all integrations tests that do not require building or running a nixos machine
|
||||
- impure integrations tests that require internet access (very rare, try to avoid)
|
||||
|
||||
- impure integrations tests that require internet access (very rare, try to
|
||||
avoid)
|
||||
|
||||
### When not to use python tests
|
||||
|
||||
- integrations tests that require building or running a nixos machine (use NixOS VM or container tests instead)
|
||||
- integrations tests that require building or running a nixos machine (use NixOS
|
||||
VM or container tests instead)
|
||||
- testing behavior of a nix function or library (use nix eval tests instead)
|
||||
|
||||
### Finding examples of python tests
|
||||
|
||||
Existing python tests in clan-core can be found by using `ripgrep`:
|
||||
|
||||
```shellSession
|
||||
rg "import pytest"
|
||||
```
|
||||
|
||||
### Locating definitions of failing python tests
|
||||
|
||||
If any python test fails in the CI pipeline, an error message like this can be found at the end of the log:
|
||||
If any python test fails in the CI pipeline, an error message like this can be
|
||||
found at the end of the log:
|
||||
|
||||
```
|
||||
...
|
||||
FAILED tests/test_machines_cli.py::test_machine_delete - clan_lib.errors.ClanError: Template 'new-machine' not in 'inputs.clan-core
|
||||
...
|
||||
```
|
||||
|
||||
In this case the test is defined in the file `/tests/test_machines_cli.py` via the test function `test_machine_delete`.
|
||||
In this case the test is defined in the file `/tests/test_machines_cli.py` via
|
||||
the test function `test_machine_delete`.
|
||||
|
||||
### Adding python tests
|
||||
|
||||
If a specific python module is tested, the test should be located near the tested module in a subdirectory called `./tests`
|
||||
If the test is not clearly related to a specific module, put it in the top-level `./tests` directory of the tested python package. For `clan-cli` this would be `/pkgs/clan-cli/clan_cli/tests`.
|
||||
All filenames must be prefixed with `test_` and test functions prefixed with `test_` for pytest to discover them.
|
||||
If a specific python module is tested, the test should be located near the
|
||||
tested module in a subdirectory called `./tests` If the test is not clearly
|
||||
related to a specific module, put it in the top-level `./tests` directory of the
|
||||
tested python package. For `clan-cli` this would be
|
||||
`/pkgs/clan-cli/clan_cli/tests`. All filenames must be prefixed with `test_` and
|
||||
test functions prefixed with `test_` for pytest to discover them.
|
||||
|
||||
### Running python tests
|
||||
|
||||
#### Running all python tests
|
||||
|
||||
To run all python tests which are executed in the CI pipeline locally, use this `nix build` command
|
||||
To run all python tests which are executed in the CI pipeline locally, use this
|
||||
`nix build` command
|
||||
|
||||
```shellSession
|
||||
nix build .#checks.x86_64-linux.clan-pytest-{with,without}-core
|
||||
@@ -168,21 +196,27 @@ nix build .#checks.x86_64-linux.clan-pytest-{with,without}-core
|
||||
To run a specific python test outside the nix sandbox
|
||||
|
||||
1. Enter the development environment of the python package, by either:
|
||||
- Having direnv enabled and entering the directory of the package (eg. `/pkgs/clan-cli`)
|
||||
- Or using the command `select-shell {package}` in the top-level dev shell of clan-core, (eg. `switch-shell clan-cli`)
|
||||
|
||||
- Having direnv enabled and entering the directory of the package (eg.
|
||||
`/pkgs/clan-cli`)
|
||||
- Or using the command `select-shell {package}` in the top-level dev shell of
|
||||
clan-core, (eg. `switch-shell clan-cli`)
|
||||
|
||||
2. Execute the test via pytest using issuing
|
||||
`pytest ./path/to/test_file.py:test_function_name -s -n0`
|
||||
|
||||
The flags `-sn0` are useful to forwards all stdout/stderr output to the terminal and be able to debug interactively via `breakpoint()`.
|
||||
`pytest ./path/to/test_file.py:test_function_name -s -n0`
|
||||
|
||||
The flags `-sn0` are useful to forwards all stdout/stderr output to the terminal
|
||||
and be able to debug interactively via `breakpoint()`.
|
||||
|
||||
### Debugging python tests
|
||||
|
||||
To debug a specific python test, find its definition (see above) and make sure to enter the correct dev environment for that python package.
|
||||
To debug a specific python test, find its definition (see above) and make sure
|
||||
to enter the correct dev environment for that python package.
|
||||
|
||||
Modify the test and add `breakpoint()` statements to it.
|
||||
|
||||
Execute the test using the flags `-sn0` in order to get an interactive shell at the breakpoint:
|
||||
Execute the test using the flags `-sn0` in order to get an interactive shell at
|
||||
the breakpoint:
|
||||
|
||||
```shelSession
|
||||
pytest ./path/to/test_file.py:test_function_name -sn0
|
||||
@@ -234,7 +268,9 @@ Failing nix eval tests look like this:
|
||||
> error: Tests failed
|
||||
```
|
||||
|
||||
To locate the definition, find the flake attribute name of the failing test near the top of the CI Job page, like for example `gitea:clan/clan-core#checks.x86_64-linux.eval-lib-values/1242`.
|
||||
To locate the definition, find the flake attribute name of the failing test near
|
||||
the top of the CI Job page, like for example
|
||||
`gitea:clan/clan-core#checks.x86_64-linux.eval-lib-values/1242`.
|
||||
|
||||
In this case `eval-lib-values` is the attribute we are looking for.
|
||||
|
||||
@@ -247,7 +283,8 @@ lib/values/flake-module.nix
|
||||
grmpf@grmpf-nix ~/p/c/clan-core (test-docs)>
|
||||
```
|
||||
|
||||
In this case the test is defined in the file `lib/values/flake-module.nix` line 21
|
||||
In this case the test is defined in the file `lib/values/flake-module.nix` line
|
||||
21
|
||||
|
||||
### Adding nix eval tests
|
||||
|
||||
@@ -255,20 +292,22 @@ In clan core, the following pattern is usually followed:
|
||||
|
||||
- tests are put in a `test.nix` file
|
||||
- a CI Job is exposed via a `flake-module.nix`
|
||||
- that `flake-module.nix` is imported via the `flake.nix` at the root of the project
|
||||
- that `flake-module.nix` is imported via the `flake.nix` at the root of the
|
||||
project
|
||||
|
||||
For example see `/lib/values/{test.nix,flake-module.nix}`.
|
||||
|
||||
### Running nix eval tests
|
||||
|
||||
Since all nix eval tests are exposed via the flake outputs, they can be ran via `nix build`:
|
||||
Since all nix eval tests are exposed via the flake outputs, they can be ran via
|
||||
`nix build`:
|
||||
|
||||
```shellSession
|
||||
nix build .#checks.x86_64-linux.{test-attr-name}
|
||||
```
|
||||
|
||||
For quicker iteration times, instead of `nix build` use the `nix-unit` command available in the dev environment.
|
||||
Example:
|
||||
For quicker iteration times, instead of `nix build` use the `nix-unit` command
|
||||
available in the dev environment. Example:
|
||||
|
||||
```shellSession
|
||||
nix-unit --flake .#legacyPackages.x86_64-linux.{test-attr-name}
|
||||
@@ -276,19 +315,23 @@ nix-unit --flake .#legacyPackages.x86_64-linux.{test-attr-name}
|
||||
|
||||
### Debugging nix eval tests
|
||||
|
||||
Follow the instructions above to find the definition of the test, then use one of the following techniques:
|
||||
Follow the instructions above to find the definition of the test, then use one
|
||||
of the following techniques:
|
||||
|
||||
#### Print debugging
|
||||
|
||||
Add `lib.trace` or `lib.traceVal` statements in order to print some variables during evaluation
|
||||
Add `lib.trace` or `lib.traceVal` statements in order to print some variables
|
||||
during evaluation
|
||||
|
||||
#### Nix repl
|
||||
|
||||
Use `nix repl` to evaluate and inspect the test.
|
||||
|
||||
Each test consists of an `expr` (expression) and an `expected` field. `nix-unit` simply checks if `expr == expected` and prints the diff if that's not the case.
|
||||
Each test consists of an `expr` (expression) and an `expected` field. `nix-unit`
|
||||
simply checks if `expr == expected` and prints the diff if that's not the case.
|
||||
|
||||
`nix repl` can be used to inspect an `expr` manually, or any other variables that you choose to expose.
|
||||
`nix repl` can be used to inspect an `expr` manually, or any other variables
|
||||
that you choose to expose.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user