Skip to main content

Create unit tests

In Dynatrace Apps, you can write unit tests using Jest. This guide teaches you how to write unit tests for React components and app functions.

Test your UI

Testing your app's UI components helps you recognize errors early on. The Strato design system offers features to help you write unit tests for your React app.

Install dependencies

First, you need to install the required dependencies:

npm i --save-dev jest ts-node ts-jest @types/jest jest-environment-jsdom @testing-library/jest-dom @testing-library/react@^12.1.5 @testing-library/user-event
  • jest: Jest is a JavaScript testing framework.
  • ts-node: Jest needs it if you want to use TypeScript for configuration. For example, jest.config.ts.
  • ts-jest: A Jest transformer that allows you to test your TypeScript code. You'll need this as you'll write Dynatrace Apps in Typescript.
  • @types/jest: Typescript types for Jest.
  • jest-environment-jsdom: This is a Jest environment where the UI tests will run.
  • @testing-library/jest-dom: Provides a set of custom matchers that you can use to extend Jest. Think of a group of methods that already have most things you need to test UI components.
  • @testing-library/react: This is a required dependency for testing Strato components.
  • @testing-library/user-event: This is a required dependency for testing Strato components.
Note

Ensure that versions for jest and jest-environment-jsdom are the same.

Add types

To get the type information in your Jest tests, you need to add Jest types in the tsconfig.json in the types array as follows:

tsconfig.json
{

"compilerOptions": {
...
"types": [
...,
"@types/jest", "@testing-library/jest-dom"
],
},
}

Create config

You write Dynatrace Apps in TypeScript, which Jest can't understand by default. Dynatrace Apps use the Strato design system, which requires custom configurations to be testable. To support both, you'll need to create a jest.config.ts file in the root directory with the following code:

jest.config.ts
import type { Config } from '@jest/types';
import { stratoPreset } from '@dynatrace/strato-components-preview/testing/jest';

const config: Config.InitialOptions = {
displayName: 'ui',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
setupFiles: ['@dynatrace/strato-components-preview/testing'],
transform: {
'^.+\\.(t|j)sx?$': ['ts-jest', { isolatedModules: true }],
},
...stratoPreset,
};

export default config;

Let's understand the config.

  • displayName: The displayName will appear in UI when running the test.
  • testEnvironment: The environment you use for testing. In this case, you need jsdom to have a browser-like environment for testing.
  • roots: Root of the project from where Jest can start looking for tests.
  • setupFiles: It's a list of modules you want to run before running each test file. They're executed before a test framework like @testing-library is loaded. In this case, you're using a setup file from the Strato design system required to test code written using Strato components.
  • transform: The transform property specifies how files should be transformed before Jest runs them. In this case, .ts, .js, .tsx, and .jsx will be transformed using ts-jest.
  • stratoPreset: It's a set of default configurations required if you're using Strato components. It also uses moduleNameMappers to ensure that dependencies are properly mapped for Jest.

Additional setup files

Depending on the features you're using, you'll need more setup files. Here's the list of SDKs with their setup files imports that you'd need to add to the setupFiles array in your jest.config.ts file:

  • @dynatrace-sdk/navigation/testing
  • @dynatrace-sdk/user-preference/testing
  • @dynatrace-sdk/app-environment/testing

Handling style imports

Jest runs your code as JavaScript. When you import styles in your React component, Jest will try to run them as JavaScript and fail. To fix this issue, you can tell Jest to mock the styles in jest.config.ts as follows:

jest.config.ts
{
...
transform: {
'.(css|scss|sass|less)$': '<rootDir>/style-mock.ts'
},
}

Add the following content in style-mock.ts:

style-mock.ts
module.exports = {
process() {
return { code: 'module.exports = {};' };
},
};

First UI unit test

To write your first UI unit test, create a file First.test.tsx in the src directory with the following content:

src/First.test.tsx
// imports
import React from 'react';
import { render, screen } from '@dynatrace/strato-components-preview/testing';

// component
import { Heading } from '@dynatrace/strato-components-preview';
const TestHeading = ({ textValue }) => {
return <Heading level={1}>{textValue}</Heading>;
};

// test
describe('Heading component', () => {
test('should render the Unit test on screen', () => {
render(<TestHeading textValue="Unit test" />);
expect(screen.getByText('Unit test')).toBeInTheDocument();
});
});

Let's understand the above test.

First, you have import statements. Besides importing React, you have the render and screen methods from the Strato design system that allow you to render a component and then create a test to see what's on the screen. You also have an import from @testing-library that extends the expect method of jest to allow you to use functions like toBeInTheDocument.

Then, you have a component named Heading that you're testing in the following test. In real applications, this wouldn't be part of the test code. However, for simplicity in this guide, it's added to the test file.

You have a test suite using the describe and test functions. These functions are part of Jest. In the test, you're using the Heading component from the Strato design system using Unit test as the heading value and expecting whether the Unit test value is printed on the screen.

Note

You'd need to import @testing-library/jest-dom in all your tests to use functions like toBeInTheDocument. However, you can use the setupFilesAfterEnv property of the Jest config to import it for all tests by default as follows:

jest.config.ts
{
...
setupFilesAfterEnv: [`<rootDir>/src/jest-setup.ts`]
}

And then import the @testing-library/jest-dom in jest-setup.ts file as following:

jest-setup.ts
import '@testing-library/jest-dom';

Now, you don't need to import this in any of your tests.

Running UI tests

To run the tests, first, you need to create an npm script in package.json as follows:

{
"scripts": {
"test:ui": "jest"
}
}

Now you can run the following command to run the tests:

npm run test:ui

And you'll be greeted by the following messages:

> jest

PASS ui src/First.test.tsx
Heading component
√ should render the Unit test on screen (23 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.311 s
Ran all test suites.
Tip

If you're using styled-components, you might get an error like ResizeObserver not found. To fix this issue, you'd need to mock ResizeObserver in jest.setup.ts as follows:

import '@testing-library/jest-dom';

global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));

Strato design system setup and clear functions

The Strato design system also provides two helper functions, setup and clear, for writing tests. By calling the setup and the clear functions, you can initialize and clear a set of mocks, which can be helpful for your tests as follows:

import { setup, clear } from '@dynatrace/strato-components-preview/testing';

setup();
// your test code here
clear();
Note

Only use the setup and clear functions if the Strato components don't behave as they should.

Test your app functions

When you run the generate function command, the Dynatrace App Toolkit will automatically create a test file. This happens for every function you generate. You also get the jest.config.js file created in your app's api directory.

All available APIs in the Dynatrace JavaScript Runtime are available in your unit tests.

Mock your fetch functions

If you're using fetch to call third party data sources from within your app functions, you need to overwrite its implementation in your unit tests. The generated test file already contains the following code snippet, which overwrites the global fetch function with the mocked fetchMock function:

const fetchMock = jest.fn();
globalThis.fetch = fetchMock;

By using the mockImplementation or mockImplementationOnce function, you can mock the behavior of your actual fetch call.

fetchMock.mockImplementationOnce(() => {
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({ id: 3, firstname: 'John', lastname: 'Doe' }),
});
});
Tip

If you have UI and app functions tests, you can use the projects property to combine UI and app functions configurations. Take a look at the projects property

Still have questions?
Find answers in the Dynatrace Community