Skip to content

Testing

Automated Testing

A lot of work went into making some automated tests for the server side. However, the testing approach is fairly non-standard for now. The tests are not all the way end-to-end. They run all the way from the service to the database. So, we really just skipped where the controllers call the services - which is pretty trivial stuff anyway.

Environment

One prerequisite: the local MySQL 8 container must be up (docker compose up -d, see First time setup). That's it — no env file, no FACILITY, no NODE_ENV.

A test target is not a deployment, so the harness (zf-server/src/testing/) deliberately bypasses the env-file/ConfigService path. Each jest worker gets its own throwaway schema (zfm_test_w<n>) on the container, built by running the real, validated migration chain; suites sharing a worker share the schema but every suite starts with all tables truncated. Connection values default to the container (127.0.0.1:3308, root) and can be overridden with TEST_DB_* variables for CI.

The retired way — a hand-made test.env selected via FACILITY=test, pointed at a benchmark database that a stray synchronize could wreck — is gone, and nothing mourns it.

Approach to Testing

All the complicated logic lies in the servers and repos, so I test there and test thoroughly. I do not worry about controllers. I just do not understand everyone saying "unit test, unit test" - I'm way too lazy to mock every little thing. Plus, the compile and test cycle is so fast, that there is really no reason to run stuff right to the database.

I number tests with big random(ish) numbers. Rationale is given below.

I also like to run against an empty database. Every test that sticks objects in the database is responsible for taking them out again. Also, every test that sticks objects in the database sticks the test number in the comment field of the object. That way it's easy to figure out what test left what crap lying around the database (by accident).

I tend to use random numbers (converted to strings) for fields that have to be unique (like names or nicknames). This reduces the chances of tests run in parallel to fight with each other. Even more so it takes all the burden of the test writers to keep coming up with uniques stings for things like mutation names.

Running Tests

cd zf-server && npm test, or the WebStorm jest tools — both hit the same jest.config.ts. The full suite runs in about a minute; expect one skipped suite (user.service.spec, pending its harness rework). Controllers are no longer entirely untested, by the way: src/auth/testing/auth-matrix.spec.ts drives real HTTP through the actual guards to pin the whole role/permission matrix.

Debugging

The webstorm tools are great. They point you at the test case and the code when things go wrong.

Note that the policy of sticking the test number in any object that goes into the database makes it super easy to track down the source of stray objects if test cases fail to clean up after themselves.

The browser walk

The client's unit suite runs in Node, where React Native's web rendering and the browser's own APIs are stubbed or absent, so a screen can pass every test and still fail to draw in Chrome. The browser walk is the check for that layer: Playwright signs in to the gui-test.test facility, visits every screen an admin can reach, opens the first record in each manager, confirms a guest and a signed-out visitor are turned away where they should be, and fails on any uncaught error, console error or request the server refused.

cd zf-react-client && npm run e2e          # against Metro's bundle, on gui-test.test
cd zf-react-client && npm run e2e:built    # exports first, then against the export, on gui-test-built.test

The two differ in what the browser is handed. Metro's bundle is what development runs; the export is what a facility is deployed with, and it goes through different processing, so a fault can live in one and not the other. The built run is the one that counts before a tag; the Metro run is the quick one while working.

Both need the gui_test facility serving, and the built run its second address too (see Serve a dev facility), and a one-time npx playwright install chromium. The walk is not part of npm test: run it on demand, and always before a release tag. It lives in zf-react-client/e2e/; a failed check leaves a trace under e2e/.results/ that npx playwright show-trace <trace.zip> replays.