LobeHub Testing Guide Quick Reference Commands:
Run specific test file
bunx vitest run --silent = 'passed-only' '[file-path]'
Database package (client)
cd packages/database && bunx vitest run --silent = 'passed-only' '[file]'
Database package (server)
- cd
- packages/database
- &&
- TEST_SERVER_DB
- =
- 1
- bunx vitest run
- --silent
- =
- 'passed-only'
- '[file]'
- Never run
- bun run test
- - it runs all 3000+ tests (~10 minutes).
- Test Categories
- Category
- Location
- Config
- Webapp
- src/*/.test.ts(x)
- vitest.config.ts
- Packages
- packages///.test.ts
- packages/*/vitest.config.ts
- Desktop
- apps/desktop/*/.test.ts
- apps/desktop/vitest.config.ts
- Core Principles
- Prefer
- vi.spyOn
- over
- vi.mock
- - More targeted, easier to maintain
- Tests must pass type check
- - Run
- bun run type-check
- after writing tests
- After 1-2 failed fix attempts, stop and ask for help
- Test behavior, not implementation details
- Basic Test Structure
- import
- {
- describe
- ,
- it
- ,
- expect
- ,
- vi
- ,
- beforeEach
- ,
- afterEach
- }
- from
- 'vitest'
- ;
- beforeEach
- (
- (
- )
- =>
- {
- vi
- .
- clearAllMocks
- (
- )
- ;
- }
- )
- ;
- afterEach
- (
- (
- )
- =>
- {
- vi
- .
- restoreAllMocks
- (
- )
- ;
- }
- )
- ;
- describe
- (
- 'ModuleName'
- ,
- (
- )
- =>
- {
- describe
- (
- 'functionName'
- ,
- (
- )
- =>
- {
- it
- (
- 'should handle normal case'
- ,
- (
- )
- =>
- {
- // Arrange → Act → Assert
- }
- )
- ;
- }
- )
- ;
- }
- )
- ;
- Mock Patterns
- // ✅ Spy on direct dependencies
- vi
- .
- spyOn
- (
- messageService
- ,
- 'createMessage'
- )
- .
- mockResolvedValue
- (
- 'id'
- )
- ;
- // ✅ Use vi.stubGlobal for browser APIs
- vi
- .
- stubGlobal
- (
- 'Image'
- ,
- mockImage
- )
- ;
- vi
- .
- spyOn
- (
- URL
- ,
- 'createObjectURL'
- )
- .
- mockReturnValue
- (
- 'blob:mock'
- )
- ;
- // ❌ Avoid mocking entire modules globally
- vi
- .
- mock
- (
- '@/services/chat'
- )
- ;
- // Too broad
- Detailed Guides
- See
- references/
- for specific testing scenarios:
- Database Model testing
- :
- references/db-model-test.md
- Electron IPC testing
- :
- references/electron-ipc-test.md
- Zustand Store Action testing
- :
- references/zustand-store-action-test.md
- Agent Runtime E2E testing
- :
- references/agent-runtime-e2e.md
- Desktop Controller testing
- :
- references/desktop-controller-test.md
- Fixing Failing Tests — Optimize or Delete?
- When tests fail due to implementation changes (not bugs), evaluate before blindly fixing:
- Keep & Fix (update test data/assertions)
- Behavior tests
-
- Tests that verify
- what
- the code does (output, side effects, user-visible behavior). Just update mock data formats or expected values.
- Example: Tool data structure changed from
- { name }
- to
- { function: { name } }
- → update mock data
- Example: Output format changed from
- Current date: YYYY-MM-DD
- to
- Current date: YYYY-MM-DD (TZ)
- → update expected string
- Delete (over-specified, low value)
- Param-forwarding tests
-
- Tests that assert exact internal function call arguments (e.g.,
- expect(internalFn).toHaveBeenCalledWith(expect.objectContaining({ exact params }))
- ) — these break on every refactor and duplicate what behavior tests already cover.
- Implementation-coupled tests
-
- Tests that verify
- how
- the code works internally rather than
- what
- it produces. If a higher-level test already covers the same behavior, the low-level test adds maintenance cost without coverage gain.
- Decision Checklist
- Does the test verify
- externally observable behavior
- (API response, DB write, rendered output)? →
- Keep
- Does the test only verify
- internal wiring
- (which function receives which params)? → Check if a behavior test already covers it. If yes →
- Delete
- Is the same behavior already tested at a
- higher integration level
- ? → Delete the lower-level duplicate
- Would the test break again on the
- next routine refactor
- ? → Consider raising to integration level or deleting
- When Writing New Tests
- Prefer
- integration-level assertions
- (verify final output) over
- white-box assertions
- (verify internal calls)
- Use
- expect.objectContaining
- only for stable, public-facing contracts — not for internal param shapes that change with refactors
- Mock at boundaries (DB, network, external services), not between internal modules
- Common Issues
- Module pollution
-
- Use
- vi.resetModules()
- when tests fail mysteriously
- Mock not working
-
- Check setup position and use
- vi.clearAllMocks()
- in beforeEach
- Test data pollution
-
- Clean database state in beforeEach/afterEach
- Async issues
- Wrap state changes in act() for React hooks