Skip to content

Testing

The engine uses Jest for both unit and end-to-end testing. Unit tests live alongside source files; E2E tests live in the test/ directory.

Unit Tests

npm run test              # run all unit tests
npm run test:watch        # watch mode
npm run test:cov          # with coverage report

Unit tests are colocated with the source files they test (e.g., my-service.spec.ts next to my-service.ts).

Mocking Dependencies

Use NestJS's Test.createTestingModule with useValue for clean dependency injection:

describe('MyService', () => {
  let service: MyService
  let mockDep: jest.Mocked<DependencyService>

  beforeEach(async () => {
    mockDep = {
      doWork: jest.fn().mockResolvedValue(result),
    } as unknown as jest.Mocked<DependencyService>

    const module = await Test.createTestingModule({
      providers: [
        MyService,
        { provide: DependencyService, useValue: mockDep },
      ],
    }).compile()

    service = module.get<MyService>(MyService)
  })
})

Testing Event Subscribers

DB subscribers are the bridge between the ORM and the event system. Test that they:

  1. Can be instantiated
  2. Subscribe to the correct entities
  3. Handle events correctly
describe('WorkflowDefinitionSubscriberService', () => {
  let service: WorkflowDefinitionSubscriberService

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [WorkflowDefinitionSubscriberService],
    }).compile()
    service = module.get(WorkflowDefinitionSubscriberService)
  })

  it('should subscribe to Workflow entities', () => {
    expect(service.getSubscribedEntities()).toContain(Workflow)
  })
})

End-to-End Tests

npm run test:e2e          # run all E2E tests
npm run test:e2e:report   # with JUnit report

Database Setup

Each E2E test gets a fresh PostgreSQL database. The setup:

  1. Creates a new database with a unique name
  2. Runs all migrations
  3. Swaps CmsClientModule for CmsClientMockModule (no real CMS needed)
  4. On test success: drops the database
  5. On test failure: retains the database for debugging
import { initNestApplication } from '../setup/init-nest-application'

describe('MyController (e2e)', () => {
  let app: INestApplication

  beforeAll(async () => {
    app = await initNestApplication()
  })

  afterAll(async () => {
    await initNestApplication.teardown(app)
  })
})

Debugging E2E Tests

To find the test database name, look for the [TESTDB] tag in test output:

[TESTDB] Setup test: WorkflowLifecycle, database name: test-e2e-c1fb8125-...

Connect to this database directly to inspect state after a failed test.

Playbook Pattern

The most powerful E2E testing approach is the Playbook pattern, described in detail in E2E Playbooks. It lets you define complete workflow execution scenarios declaratively.