Skip to main content

serverpod-testing

Test Serverpod endpoints and business logic - withServerpod, sessionBuilder, authentication, DB seeding, rollback, streams, running tests. Use when writing server tests or working with serverpod_test.

Quellinformationen

Repository
serverpod/wonky
Letzte Quellaktivität
20. Oktober 2026 um 10:16
Erkannte Sprache von SKILL.md
Englisch
Sterne
6
Forks
5

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
serverpod-testing
description
Test Serverpod endpoints and business logic - withServerpod, sessionBuilder, authentication, DB seeding, rollback, streams, running tests. Use when writing server tests or working with serverpod_test.
# Serverpod Testing Generated test tools let you call endpoints in tests with full server context (DB, caching, etc.). Import the **generated** test tools file, not `serverpod_test` directly - it re-exports everything needed. The import path comes from `config/generator.yaml` (`server_test_tools_path`); the examples below use the common `test_tools/serverpod_test_tools.dart` output. Prefer Given/when/then descriptions. Across nested groups plus the test name, there should be one clear Given, one when, and one then that can explain a failure without reading the code. ## Basic test ```dart import 'package:test/test.dart'; import 'test_tools/serverpod_test_tools.dart'; void main() { withServerpod('Given Greeting endpoint', (sessionBuilder, endpoints) { test('when calling hello then returns greeting', () async { final greeting = await endpoints.greeting.hello(sessionBuilder, 'Bob'); expect(greeting.message, 'Hello Bob'); }); }); } ``` ## Session builder Use `sessionBuilder.copyWith(...)` to create modified sessions. Call `sessionBuilder.build()` to get a `Session` for DB operations or passing to helpers. ### Authenticated tests ```dart withServerpod('Given AuthEndpoint', (sessionBuilder, endpoints) { final userId = '550e8400-e29b-41d4-a716-446655440000'; group('when authenticated', () { var authed = sessionBuilder.copyWith( authentication: AuthenticationOverride.authenticationInfo(userId, {Scope('user')}), ); test('then hello succeeds', () async { final greeting = await endpoints.authExample.hello(authed, 'Michael'); expect(greeting, 'Hello, Michael!'); }); }); group('when unauthenticated', () { var unauthed = sessionBuilder.copyWith( authentication: AuthenticationOverride.unauthenticated(), ); test('then hello throws', () async { await expectLater( endpoints.authExample.hello(unauthed, 'Michael'), throwsA(isA<ServerpodUnauthenticatedException>()), ); }); }); }); ``` ### Seeding the database ```dart withServerpod('Given Products endpoint', (sessionBuilder, endpoints) { var session = sessionBuilder.build(); setUp(() async { await Product.db.insert(session, [ Product(name: 'Apple', price: 10), Product(name: 'Banana', price: 10), ]); }); test('then all returns both products', () async { final products = await endpoints.products.all(sessionBuilder); expect(products, hasLength(2)); }); }); ``` No manual tearDown needed - by default each test runs in a transaction that is rolled back. ## Rollback behavior Default: `RollbackDatabase.afterEach` - each test in a rolled-back transaction. - `afterAll` - roll back after all tests in the group. Useful for scenario tests where consecutive tests depend on each other and setup is expensive. - `disabled` - no automatic rollback. Required when endpoint code uses concurrent `session.db.transaction(...)` calls (nested transactions would throw `InvalidConfigurationException`). Each `withServerpod` group gets its own database, so committed data never leaks into other groups and no manual cleanup or `--concurrency=1` is needed; clean up in `tearDown` only if later tests in the same group need a clean slate. ```dart withServerpod( 'Given concurrent transactions', (sessionBuilder, endpoints) { test('then should commit all', () async { await endpoints.products.concurrentTransactionCalls(sessionBuilder); }); }, rollbackDatabase: RollbackDatabase.disabled, ); ``` ## Testing business logic with Session If logic lives outside endpoints but needs a `Session`, use `withServerpod` and ignore the endpoints parameter: ```dart withServerpod('Given product quantity is zero', (sessionBuilder, _) { var session = sessionBuilder.build(); setUp(() async { await Product.db.insertRow(session, Product(id: 123, name: 'Apple', quantity: 0)); }); test('then decreasing throws', () async { await expectLater( ProductsBusinessLogic.updateQuantity(session, id: 123, decrease: 1), throwsA(isA<InvalidOperationException>()), ); }); }); ``` ## Testing streams Use `flushEventQueue()` to ensure a generator executes up to its `yield` before asserting: ```dart withServerpod('Given shared stream', (sessionBuilder, endpoints) { final user1 = sessionBuilder.copyWith( authentication: AuthenticationOverride.authenticationInfo('user-1', {})); final user2 = sessionBuilder.copyWith( authentication: AuthenticationOverride.authenticationInfo('user-2', {})); test('when posting numbers then listener receives them', () async { var stream = endpoints.comm.listenForNumbers(user1); await flushEventQueue(); // Wait for stream to register await endpoints.comm.postNumber(user2, 111); await endpoints.comm.postNumber(user2, 222); await expectLater(stream.take(2), emitsInOrder([111, 222])); }); }); ``` ## withServerpod options The ones that change behavior most often: - `rollbackDatabase` — `afterEach` (default), `afterAll`, or `disabled`. - `ephemeralDatabase` — own empty database per group, `true` by default. Set to `false` to use the configured database, including a previously seeded one. It is not created or dropped, and groups that share it cannot run in parallel. - `applyMigrations` — apply pending migrations on start, `true` by default. - `runMode` — `ServerpodRunMode.test` by default, so `config/test.yaml` is loaded. - `configOverride` — adjust the loaded config, e.g. to point at another database. The full list, with defaults, is in [`references/with-serverpod-options.md`](references/with-serverpod-options.md). ## Running tests ```bash dart test # All tests dart test -t integration # Only integration tests dart test -x integration # Only unit tests ``` **Nothing has to be started first — do NOT run `docker compose up`.** `withServerpod` boots the server in the `test` run mode, and the test database comes with it: projects created by `serverpod create` use an embedded PostgreSQL that Serverpod launches and manages, or a SQLite file. A generated `docker-compose.yaml` is present even in those projects, so seeing that file is not a reason to start Docker. Only when a run actually fails to reach the database is it worth opening `config/test.yaml`: a `database` section with `host`/`port` and no `dataPath` points at an external database, which does have to be running (`docker compose up -d` starts the one the project ships). With `dataPath` or `filePath` set, fix the configuration or runtime instead — starting Docker will not help. ## Project structure Keep tests organized: - `test/unit/` - unit tests (no Serverpod dependency) - `test/integration/` - tests using `withServerpod` Always call endpoints via the `endpoints` parameter, not by instantiating endpoint classes directly - the test tools handle lifecycle and validation to match production behavior.
Auf GitHub ansehen