In addition to Unit and Integration tests, you can also write tests that interact with your application as a real user would. That technique is called End to End(E2E) testing.

In this example we have a workspace with two members:

  • server: a web server that uses Axum for HTTP and Sqlx connect to an instance of PostgreSQL
  • e2e: a end-to-end test "script" that drives Firefox into interacting with the sever

Quick-start an E2E project in a fresh directory with:

nix flake init -t github:ipetkov/crane#end-to-end-testing

Alternatively, if you have an existing project already, copy and paste the following flake.nix and modify it to build your workspace's packages:

{ description = "Example E2E testing"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; crane.url = "github:ipetkov/crane"; flake-utils.url = "github:numtide/flake-utils"; rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { nixpkgs, crane, flake-utils, rust-overlay, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; overlays = [ (import rust-overlay) ]; }; inherit (pkgs) lib; craneLib = (crane.mkLib pkgs).overrideToolchain (p: p.rust-bin.stable.latest.default); src = craneLib.cleanCargoSource ./.; workspace = craneLib.buildPackage { inherit src; doCheck = false; nativeBuildInputs = lib.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ pkgs.libiconv CoreFoundation Security SystemConfiguration ]); }; # The script inlined for brevity, consider extracting it # so that it becomes independent of nix runE2ETests = pkgs.runCommand "e2e-tests" { nativeBuildInputs = with pkgs; [ retry curl geckodriver firefox cacert postgresql ]; } '' wait-for-connection() { timeout 5s \ retry --until=success --delay "1" -- \ curl -s "$@" } initdb postgres-data pg_ctl --pgdata=postgres-data --options "-c unix_socket_directories=$PWD" start export DATABASE_URL="postgres:///postgres?host=$PWD" psql "$DATABASE_URL" <<EOF CREATE TABLE users(name TEXT); EOF ${workspace}/bin/server & wait-for-connection --fail localhost:8000 # Firefox likes to write to $HOME HOME="$(mktemp -d)" geckodriver & wait-for-connection localhost:4444 ${workspace}/bin/e2e_tests touch $out ''; in { checks = { inherit workspace; # Firefox is broken in some platforms (namely "aarch64-apple-darwin"), skip those } // (lib.optionalAttrs (lib.meta.availableOn system pkgs.firefox) { inherit runE2ETests; }); devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ rustc cargo ] ++ (lib.optionals (!pkgs.stdenv.isDarwin) [ geckodriver firefox ]); }; }); }