1# Testers {#chap-testers}
2This chapter describes several testing builders which are available in the `testers` namespace.
3
4## `hasPkgConfigModules` {#tester-hasPkgConfigModules}
5
6<!-- Old anchor name so links still work -->
7[]{#tester-hasPkgConfigModule}
8Checks whether a package exposes a given list of `pkg-config` modules.
9If the `moduleNames` argument is omitted, `hasPkgConfigModules` will
10use `meta.pkgConfigModules`.
11
12Example:
13
14```nix
15passthru.tests.pkg-config = testers.hasPkgConfigModules {
16 package = finalAttrs.finalPackage;
17 moduleNames = [ "libfoo" ];
18};
19```
20
21If the package in question has `meta.pkgConfigModules` set, it is even simpler:
22
23```nix
24passthru.tests.pkg-config = testers.hasPkgConfigModules {
25 package = finalAttrs.finalPackage;
26};
27
28meta.pkgConfigModules = [ "libfoo" ];
29```
30
31## `testVersion` {#tester-testVersion}
32
33Checks the command output contains the specified version
34
35Although simplistic, this test assures that the main program
36can run. While there's no substitute for a real test case,
37it does catch dynamic linking errors and such. It also provides
38some protection against accidentally building the wrong version,
39for example when using an 'old' hash in a fixed-output derivation.
40
41Examples:
42
43```nix
44passthru.tests.version = testers.testVersion { package = hello; };
45
46passthru.tests.version = testers.testVersion {
47 package = seaweedfs;
48 command = "weed version";
49};
50
51passthru.tests.version = testers.testVersion {
52 package = key;
53 command = "KeY --help";
54 # Wrong '2.5' version in the code. Drop on next version.
55 version = "2.5";
56};
57
58passthru.tests.version = testers.testVersion {
59 package = ghr;
60 # The output needs to contain the 'version' string without any prefix or suffix.
61 version = "v${version}";
62};
63```
64
65## `testBuildFailure` {#tester-testBuildFailure}
66
67Make sure that a build does not succeed. This is useful for testing testers.
68
69This returns a derivation with an override on the builder, with the following effects:
70
71 - Fail the build when the original builder succeeds
72 - Move `$out` to `$out/result`, if it exists (assuming `out` is the default output)
73 - Save the build log to `$out/testBuildFailure.log` (same)
74
75Example:
76
77```nix
78runCommand "example" {
79 failed = testers.testBuildFailure (runCommand "fail" {} ''
80 echo ok-ish >$out
81 echo failing though
82 exit 3
83 '');
84} ''
85 grep -F 'ok-ish' $failed/result
86 grep -F 'failing though' $failed/testBuildFailure.log
87 [[ 3 = $(cat $failed/testBuildFailure.exit) ]]
88 touch $out
89'';
90```
91
92While `testBuildFailure` is designed to keep changes to the original builder's
93environment to a minimum, some small changes are inevitable.
94
95 - The file `$TMPDIR/testBuildFailure.log` is present. It should not be deleted.
96 - `stdout` and `stderr` are a pipe instead of a tty. This could be improved.
97 - One or two extra processes are present in the sandbox during the original
98 builder's execution.
99 - The derivation and output hashes are different, but not unusual.
100 - The derivation includes a dependency on `buildPackages.bash` and
101 `expect-failure.sh`, which is built to include a transitive dependency on
102 `buildPackages.coreutils` and possibly more. These are not added to `PATH`
103 or any other environment variable, so they should be hard to observe.
104
105## `testEqualContents` {#tester-equalContents}
106
107Check that two paths have the same contents.
108
109Example:
110
111```nix
112testers.testEqualContents {
113 assertion = "sed -e performs replacement";
114 expected = writeText "expected" ''
115 foo baz baz
116 '';
117 actual = runCommand "actual" {
118 # not really necessary for a package that's in stdenv
119 nativeBuildInputs = [ gnused ];
120 base = writeText "base" ''
121 foo bar baz
122 '';
123 } ''
124 sed -e 's/bar/baz/g' $base >$out
125 '';
126}
127```
128
129## `testEqualDerivation` {#tester-testEqualDerivation}
130
131Checks that two packages produce the exact same build instructions.
132
133This can be used to make sure that a certain difference of configuration,
134such as the presence of an overlay does not cause a cache miss.
135
136When the derivations are equal, the return value is an empty file.
137Otherwise, the build log explains the difference via `nix-diff`.
138
139Example:
140
141```nix
142testers.testEqualDerivation
143 "The hello package must stay the same when enabling checks."
144 hello
145 (hello.overrideAttrs(o: { doCheck = true; }))
146```
147
148## `invalidateFetcherByDrvHash` {#tester-invalidateFetcherByDrvHash}
149
150Use the derivation hash to invalidate the output via name, for testing.
151
152Type: `(a@{ name, ... } -> Derivation) -> a -> Derivation`
153
154Normally, fixed output derivations can and should be cached by their output
155hash only, but for testing we want to re-fetch everytime the fetcher changes.
156
157Changes to the fetcher become apparent in the drvPath, which is a hash of
158how to fetch, rather than a fixed store path.
159By inserting this hash into the name, we can make sure to re-run the fetcher
160every time the fetcher changes.
161
162This relies on the assumption that Nix isn't clever enough to reuse its
163database of local store contents to optimize fetching.
164
165You might notice that the "salted" name derives from the normal invocation,
166not the final derivation. `invalidateFetcherByDrvHash` has to invoke the fetcher
167function twice: once to get a derivation hash, and again to produce the final
168fixed output derivation.
169
170Example:
171
172```nix
173tests.fetchgit = testers.invalidateFetcherByDrvHash fetchgit {
174 name = "nix-source";
175 url = "https://github.com/NixOS/nix";
176 rev = "9d9dbe6ed05854e03811c361a3380e09183f4f4a";
177 hash = "sha256-7DszvbCNTjpzGRmpIVAWXk20P0/XTrWZ79KSOGLrUWY=";
178};
179```
180
181## `runNixOSTest` {#tester-runNixOSTest}
182
183A helper function that behaves exactly like the NixOS `runTest`, except it also assigns this Nixpkgs package set as the `pkgs` of the test and makes the `nixpkgs.*` options read-only.
184
185If your test is part of the Nixpkgs repository, or if you need a more general entrypoint, see ["Calling a test" in the NixOS manual](https://nixos.org/manual/nixos/stable/index.html#sec-calling-nixos-tests).
186
187Example:
188
189```nix
190pkgs.testers.runNixOSTest ({ lib, ... }: {
191 name = "hello";
192 nodes.machine = { pkgs, ... }: {
193 environment.systemPackages = [ pkgs.hello ];
194 };
195 testScript = ''
196 machine.succeed("hello")
197 '';
198})
199```
200
201## `nixosTest` {#tester-nixosTest}
202
203Run a NixOS VM network test using this evaluation of Nixpkgs.
204
205NOTE: This function is primarily for external use. NixOS itself uses `make-test-python.nix` directly. Packages defined in Nixpkgs [reuse NixOS tests via `nixosTests`, plural](#ssec-nixos-tests-linking).
206
207It is mostly equivalent to the function `import ./make-test-python.nix` from the
208[NixOS manual](https://nixos.org/nixos/manual/index.html#sec-nixos-tests),
209except that the current application of Nixpkgs (`pkgs`) will be used, instead of
210letting NixOS invoke Nixpkgs anew.
211
212If a test machine needs to set NixOS options under `nixpkgs`, it must set only the
213`nixpkgs.pkgs` option.
214
215### Parameter {#tester-nixosTest-parameter}
216
217A [NixOS VM test network](https://nixos.org/nixos/manual/index.html#sec-nixos-tests), or path to it. Example:
218
219```nix
220{
221 name = "my-test";
222 nodes = {
223 machine1 = { lib, pkgs, nodes, ... }: {
224 environment.systemPackages = [ pkgs.hello ];
225 services.foo.enable = true;
226 };
227 # machine2 = ...;
228 };
229 testScript = ''
230 start_all()
231 machine1.wait_for_unit("foo.service")
232 machine1.succeed("hello | foo-send")
233 '';
234}
235```
236
237### Result {#tester-nixosTest-result}
238
239A derivation that runs the VM test.
240
241Notable attributes:
242
243 * `nodes`: the evaluated NixOS configurations. Useful for debugging and exploring the configuration.
244
245 * `driverInteractive`: a script that launches an interactive Python session in the context of the `testScript`.