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.

소스 정보

저장소
serverpod/wonky
최근 소스 활동
2026년 10월 20일 10:16
감지된 SKILL.md 언어
영어
스타
6
포크
5

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
2 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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.
GitHub에서 보기