Running Unit and Acceptance Tests¶
In any process, obsessing about the wait times will yield greater enhancements than practically anything else, for longer than you might think. Automation, simplification, etc. are implementation details of that obsession. — Dan North
Unit Tests¶
Go¶
To run all unit tests, type make test in a terminal inside the development environment.
The following make targets are currently defined for tests:
test: Executes all Go (/pkg,/internal, and theinternalpackages of the editions), JavaScript (frontend/tests), and shell script unit tests on SQLite, with a 20 min timeout per Go packagetest-go,test-js: Run the Go or JavaScript unit tests on their owntest-integration: Same astest-go, plus theintegrationbuild tag for the Insta360 stacking, import, and reconciliation matricestest-short: Executes only fast Go tests with a 5 min timeouttest-race: Same astest-gobut with the race condition detector enabled (much slower) and a 60 min timeouttest-coverage: Same astest-gobut creates a coverage.txt file and a human-readable coverage.html report; timeout is elevated to 30 mintest-mariadb: Runs the Go tests against a MariaDB instance instead of SQLitetest-postgres: Runs the Go tests against the optional PostgreSQL service, which must be started first withdocker compose --profile postgres up -d; it first runsreset-postgres-testdb, which drops thetestdb_*databases left by earlier runsreset-testdb: Deletes the SQLite test database files and drops thetestdb_*MariaDB databases the Go tests create;test-mariadbrunsreset-mariadb-testdbautomatically, so call it by hand after an interrupted or targeted run- Subset targets for focused runs:
test-pkg,test-api,test-ai,test-entity,test-commands,test-photoprism,test-hub,test-video
The Go targets build with the tags in GOTEST_TAGS, which defaults to slow,develop. Override it on the make command line, for example make test-mariadb GOTEST_TAGS=slow,develop,integration; an exported environment variable is ignored.
You can run single tests via go test -run in a package directory, e.g. /internal/photoprism:
$ go test -run NameOfTest
See docs for more info.
ONNX Runtime¶
The bundled face detection and embedding models run on the ONNX Runtime, so tests that exercise face detection, face embeddings, or the configuration that resolves them need the shared library to be present. The development container ships it, which is the main reason to run the Go tests inside the container rather than on the host: without it those models cannot be loaded, and the affected assertions fail in a way that reads like a code regression.
The library is loaded at startup instead of being linked into the binary, and the bindings ask for the exact C API version of the headers they vendor. An older runtime therefore fails to load rather than running with reduced functionality, and reports that the requested API version is not available. A stale development image is the usual cause, so rebuild or re-pull it before looking for the fault in the code. Outside Docker, scripts/dist/install-onnx.sh installs a matching version.
Test Frameworks¶
Go comes with a cool testing framework, it allows you to write test code using the same language, without needing to learn any library or test engine. Go advanced testing tips & tricks contains a lot of useful information. We only import testify/assert to save a few lines for common assertions.
Todo: Use a SQL mock driver to test database interactions, for example DATA-DOG/go-sqlmock.
Slow Tests¶
Slow tests and benchmarks can be skipped using the -short flag:
func TestTimeConsuming(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
...
}
To execute:
go test -short
JavaScript¶
To run all JavaScript unit tests, type make test-js in a terminal.
Alternatively, from the frontend directory, run npm run test. Both set TZ=UTC and NODE_ENV for Vitest, so a bare npx vitest run reports date and component test failures that do not occur otherwise.
In case you want to run a single test, add .only to the test you want to run e.g.:
import { describe, it, expect } from 'vitest';
describe('Album', () => {
it.only('should get album id', () => {
const album = new Album({ AlbumUUID: 66 });
expect(album.getId()).toBe('66');
});
});
You can also run a single file or filter by test name:
cd frontend && npm run test -- tests/vitest/common/util.test.js
cd frontend && npm run test -- -t "should get album id"
For watch mode:
make vitest-watch
Available Make targets for JavaScript tests:
make test-js: Run all JavaScript unit testsmake vitest-watch: Run Vitest in watch modemake vitest-coverage: Run Vitest with coverage reportmake vitest-component: Run component tests
Test coverage output is saved to frontend/coverage/index.html.
Test Frameworks¶
To test JavaScript code we use Vitest together with Vite, jsdom, Vue Test Utils, and @vitest/coverage-v8.
Acceptance Tests¶
Before you proceed run
make dep-acceptance
Running Tests Inside the Docker Container¶
In the development container environment, you can run the tests in headless chrome:
make acceptance-run-chromium
To run the acceptance tests on MariaDB or PostgreSQL instead of SQLite, pass the database on the make command line, e.g. make acceptance-run-chromium DB=mariadb or DB=postgres (requires the postgres profile); a DB environment variable is ignored. Add TC_USE_VIDEO=1 to record videos of failed tests in frontend/tests/acceptance/videos/.
Test Frameworks¶
Our goal was to implement UI acceptance tests using JavaScript, so that frontend developers are able to run and write them without learning Go.
To make a final decision, we compared TestCafe, Cypress and Nightwatch.js. We agreed on using TestCafe as tests were the most stable and pretty fast (because no long timeouts are needed).
| Feature | TestCafe | Cypress | Nightwatch.js |
|---|---|---|---|
| Supported Browsers Local | Chrome, Firefox, Opera, Safari, Internet Explorer, Microsoft Edge, Google Chrome Canary, Chromium |
Chrome, Chromium, Chrome Canary, Electron |
Geckodriver, Chromedriver, Microsoft Webdriver, Safaridriver |
| Supported Browsers Headless | Chrome, Firefox |
Electron | Problems running headless |
| Continuous Integration Support | yes | yes | yes |
| Setup | easy via npm | easy via npm | easy via npm |
| Usability | +++ | +++ | ++ |
| Speed (3 tests) | 2 min (headless chrome and firefox) 1 min (only chrome headless) 1 min (chrome headed) |
5 min (headless electron) 2,5 min (chrome headed) |
7 min (chrome headed) headless not working |
| Stability | nice | unstable --> waiting times needed | unstable --> waiting times needed |
| Documentation | +++ | ++ | ++ |
| Notes | easy to find elements | easy to find elements | additional library needed to find selectors by text |
Other test libraries and frameworks we currently don't use: