Testing Services (WKC)

Well known component services are servers built with the well known component architecture, where each part of the service is modularized and encapsulated in a component. These components, alongside the ability of being easy to be interchanged, have the ability of being easy to unit test, as all of their dependencies are injected when created.

Testing these services SHOULD be done using two different types of test, unit and integration. Components that contain logic MUST be tested using unit tests and components that have mostly interactions with external services SHOULD be tested using integrations tests. That is, a database adapter component which does not contain any logic and contains mostly database queries MUST be tested integratedly.

Unit testing well known component components

All components MUST be unit tested and MUST be written following the section defined here on how to write tests.

To show how these component are tested, here's a simple component with only one method and two dependencies, settings and friends:

// Voice component

export function createVoiceComponent(dependencies: Pick<AppComponents, 'settings' | 'friends'>) {
	const { settings, friends } = dependencies

  async function canCallEachOther(caller: string, callee: string): Promise<boolean> {
	  const [callerSettings, calleeSetting] = Promise.all([settings.getPrivacySetting(caller), settings.getPrivacySetting(callee)])
	  const areFriends = await friends.areUsersFriends(caller, callee)
		return areFriends || (callerSettings !== Settings.ONLY_FRIENDS && calleeSettings !== Settings.ONLY_FRIENDS)
  }
  
  return {
	  createVoiceChat
  }
}

In order to test this component, proper mocks must be built, that is, mocks that have the correct types to interface with the component to be tested and that are flexible to allow us to create all types of tests. These mocks should also be immutable between tests, preserving the idea that each test is executed in its own context, isolated from the others.

These mocks SHOULD be created using component mock creator functions and SHOULD be named as createComponentNameMockedComponent. Taking as an example the component created above, here's an example on how they SHOULD look like:

By using these mocks carefully placed inside a beforeEach, we can be sure that the mocks are defined correctly isolated from different executions. Here's an example on how these mocks can be used:

This way of defining the mocks not only allows us to correctly isolate the contexts of the tests between executions, but also builds the grounds for a proper context initialization, as by having the mocked functions already defined, it's quite easy to build each context:

Integration testing

All endpoints, WS RPC calls, jobs or task MUST be integrally tested and MUST be written following the section defined here on how to write tests. Integration tests must be placed under the test/integration directory and must be named in accordance with the entry point we want to test.

Integration tests should be limited to testing either specific integration conditions that can't be tested with unit tests (SQL queries, Redis operations, etc) and the integration between our logic components and the protocol (HTTP, WS).

To show an example on how integration tests must be made, we'll be testing a simple HTTP request that retrieves a user's friends.

And a friends component having the following:

Last updated