Skip to content

Testing

This chapter describes the strategy for unit and end-to-end testing and the tools used for testing.

Unit Testing

Unit tests are realized using the jest framework. The unit tests are located along the files that are to be tested, if possible. To run the tests, execute the following command:

npm run test

Mocking

For testing services with dependencies, follow this pattern:

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

  beforeEach(async () => {
    mockDependency = {
      method: jest.fn().mockResolvedValue(testData)
    } as unknown as jest.Mocked<DependencyService>

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

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

Testing Event Subscribers

Event subscribers like WorkflowDefinitionSubscriberService should be tested to:

  1. Verify they can be instantiated
  2. Check which entities they subscribe to
  3. Test their event handling logic

Basic subscriber test example:

describe('WorkflowDefinitionSubscriberService', () => {
  let service: WorkflowDefinitionSubscriberService

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

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

  it('should be defined', () => {
    expect(service).toBeDefined()
  })

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

Debugging of Unit Tests

To debug the unit tests, you can connect the debugger to the running test process.

End-to-End Testing

End-to-End testing is realized using the jest framework. The tests are located in the test directory. To run the tests, execute the following command:

npm run test:e2e

Custom setup and teardown methods are provided. The setup method creates a new empty database that can be used for testing. This database is destroyed after the tests are successfully finished. Only for failed tests, the database is not destroyed to allow for debugging.

To use this mechanism, add the following lines to your test file:

import { INestApplicationWithServer } from 'test/setup/init-test'
import request from 'supertest'
import { initNestApplication } from './setup/init-test'
import { teardownTest } from './setup/teardown-test'

describe('ExampleHealthController (e2e)', () => {
  let app: INestApplicationWithServer

  beforeEach(async () => {
    app = await initNestApplication(expect.getState())
  })

  afterEach(async () => {
    await teardownTest(app, expect.getState())
  })

  it('/health (GET)', async () => {
    await request(app.getHttpServer()).get('/health').expect(200).expect({ healthy: true })
  })
})

Debugging of End-to-End Tests

To debug the end-to-end tests, you can connect the debugger to the running test process. If you want to examine the database, you can search for the [TESTDB] tag in the logs. These look like this:

[TESTDB] Setup test: HealthController (e2e) /health (GET), database name: test-e2e-c1fb8125-0e47-4267-8c6e-d1f9d1498b40

where test-e2e-c1fb8125-0e47-4267-8c6e-d1f9d1498b40 is the name of the database. For failing tests, the databases are retained.