Most of it was focused around running the tests instead of how to create/modify them. Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
6.2 KiB
Tests
Home Manager includes a basic test suite and it is highly recommended to include at least one test when adding a module. Tests are typically in the form of "golden tests" where, for example, a generated configuration file is compared to a known correct file.
It is relatively easy to create tests by modeling the existing tests,
found in the tests project directory.
Writing Basic Tests
Home Manager tests use the NMT framework, which provides a set of assertion functions to verify that modules generate the expected files and configurations. Tests are written as Nix expressions that define both the Home Manager configuration and the test assertions.
Test Structure
A basic test file structure looks like:
{
# Home Manager configuration
programs.myprogram = {
enable = true;
settings = {
option = "value";
};
};
# NMT test script with assertions
nmt.script = ''
assertFileExists "home-files/.config/myprogram/config.toml"
assertFileContent "home-files/.config/myprogram/config.toml" ${./expected-config.toml}
'';
}
Test Organization
Tests are organized in the tests directory structure:
tests/modules/programs/myprogram/default.nix- Lists all test cases for the moduletests/modules/programs/myprogram/basic-configuration.nix- A basic test casetests/modules/programs/myprogram/expected-config.toml- Expected output file
The default.nix file should list all test cases:
{
myprogram-basic-configuration = ./basic-configuration.nix;
myprogram-empty-settings = ./empty-settings.nix;
}
Common NMT Assertions
NMT provides several assertion functions:
assertFileExists "path"- Verify a file was createdassertPathNotExists "path"- Verify a file was NOT createdassertFileContent "path" expected-file- Compare file contentsassertFileRegex "path" "regex"- Check file matches regex
For a full reference to the functions available in test scripts, you can look at NMT's bash-lib.
Practical Examples
Here are some real-world examples of common test patterns:
Testing that a configuration file is generated:
{
programs.alacritty = {
enable = true;
settings.font.size = 12;
};
nmt.script = ''
assertFileExists "home-files/.config/alacritty/alacritty.yml"
assertFileContains "home-files/.config/alacritty/alacritty.yml" "size: 12"
'';
}
Testing that no files are created when disabled:
{
programs.alacritty.enable = false;
nmt.script = ''
assertPathNotExists "home-files/.config/alacritty"
'';
}
Testing exact file content against expected output:
{
programs.fastfetch = {
enable = true;
settings.display.color = "blue";
};
nmt.script =
let
configFile = "home-files/.config/fastfetch/config.jsonc";
in
''
assertFileExists "${configFile}"
assertFileContent "${configFile}" ${./expected-config.jsonc}
'';
}
Testing multiple conditions in one test:
{
programs.myprogram = {
enable = true;
configFile = "custom.conf";
extraConfig = "debug = true";
};
nmt.script = ''
assertFileExists "home-files/.config/myprogram/custom.conf"
assertFileRegex "home-files/.config/myprogram/custom.conf" "debug = true"
assertFileRegex "home-files/.config/myprogram/custom.conf" "^# Generated by Home Manager"
'';
}
Platform-Specific Tests
When a module is platform-specific (Linux-only or Darwin-only), the test's default.nix
file should use lib.optionalAttrs to conditionally expose tests based on the platform.
This prevents evaluation errors on unsupported platforms during the test suite runs.
Linux-only module tests:
{ lib, pkgs, ... }:
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
rofi-valid-config = ./valid-config.nix;
rofi-custom-theme = ./custom-theme.nix;
}
Darwin-only module tests:
{ lib, pkgs, ... }:
lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin {
sketchybar-basic = ./basic-configuration.nix;
sketchybar-lua-config = ./lua-config.nix;
}
For cross-platform modules that have packages which need to be stubbed on Darwin,
add the package names to tests/darwinScrublist.nix to prevent build failures
during cross-platform test runs.
Using the tests command
Home Manager provides a convenient tests command for discovering and running tests:
# List all available tests
$ nix run .#tests -- -l
# List tests matching a pattern
$ nix run .#tests -- -l alacritty
# Run all tests matching a pattern
$ nix run .#tests -- alacritty
# Run a specific test
$ nix run .#tests -- test-alacritty-empty-settings
# Run integration tests
$ nix run .#tests -- -t -l
# Interactive test selection (requires fzf)
$ python3 tests/tests.py -i
# Pass additional nix build flags
$ nix run .#tests -- alacritty -- --verbose
Manual test commands
For advanced usage or CI environments, you can also run tests manually using nix build commands.
The full Home Manager test suite can be run by executing
$ nix-build --pure --option allow-import-from-derivation false tests -A build.all
in the project root. List all test cases through
$ nix-build --pure tests --option allow-import-from-derivation false -A list
and run an individual test, for example alacritty-empty-settings,
through
$ nix-build --pure tests --option allow-import-from-derivation false -A build.alacritty-empty-settings
However, those invocations will impurely source the system's Nixpkgs,
and may cause failures. To run against the Nixpkgs from the flake.lock file,
use instead e.g.
$ nix build --reference-lock-file flake.lock --option allow-import-from-derivation false ./tests#test-all
or
$ nix build --reference-lock-file flake.lock --option allow-import-from-derivation false ./tests#test-alacritty-empty-settings
Some tests may be marked with enableLegacyIfd, those may be run by run with e.g.
$ nix-build --pure tests --arg enableLegacyIfd true -A build.mytest