Create unit tests
- How-to
- 7 minutes
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
Here are the dependencies you need to install:
jest
: Jest is a JavaScript testing framework.ts-node
: Needed to use TypeScript for configuration, such asjest.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
: 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 of the things you need to test UI components.@testing-library/react
: A required dependency for testing Strato components.@testing-library/user-event
: A required dependency for testing Strato components.
To install the dependencies, run the folowing command:
npm i --save-dev jest ts-node ts-jest @types/jest jest-environment-jsdom @testing-library/jest-dom @testing-library/react @testing-library/user-event
If you're using React 17 or lower, use the following command 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
After installing the dependencies, ensure that the 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:
{
"compilerOptions": {
...
"types": [
...,
"@types/jest", "@testing-library/jest-dom"
],
},
}
Create a configuration file
Dynatrace Apps are written in TypeScript, which Jest can't understand by default. Moreover, Dynatrace Apps are built with the Strato design system, which requires custom configurations to be testable. To run unit tests using Jest, you need to create a jest.config.js
file in the root directory of your app with the following code:
const { stratoPreset } = require('@dynatrace/strato-components-preview-testing/jest/preset');
/** @type {import('jest').Config} */
module.exports = {
...stratoPreset,
preset: 'ts-jest',
displayName: 'ui',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
transform: {
'^.+\\.(t|j)sx?$': ['ts-jest', { isolatedModules: true }],
},
moduleNameMapper: {
...stratoPreset.moduleNameMapper,
// your other moduleNameMappers
},
};
Here's a description of the settings in the configuration file:
- displayName—the
displayName
that appears 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—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—specifies how Jest should transform the files before running them. In this case,
.ts
,.js
,.tsx
, and.jsx
are transformed usingts-jest
. - stratoPreset—a set of default configurations required if you're using Strato components. It also uses
moduleNameMappers
, which maps dependencies correctly for Jest.
Additional setup files
Depending on the features you use, you may need additional setup files. Below are the SDKs with their setup file imports, which you may need to add to the setupFiles
array in your jest.config.js
file:
@dynatrace-sdk/navigation/testing
@dynatrace-sdk/user-preferences/testing
@dynatrace-sdk/app-environment/testing
@dynatrace-sdk/error-handlers/testing
Handling style imports
By default, Jest runs your code as JavaScript. When you import styles into your React component, Jest tries to interpret them as JavaScript and fail. To fix this issue, you can tell Jest to mock the styles in jest.config.js
, as follows:
{
...
transform: {
'.(css|scss|sass|less)$': '<rootDir>/style-mock.ts'
},
}
Add the following content in style-mock.ts
:
module.exports = {
process() {
return { code: 'module.exports = {};' };
},
};
Your 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:
// imports
import React from 'react';
import { render } from '@dynatrace/strato-components-preview-testing/jest';
import { screen } from '@testing-library/react';
// component
import { Heading } from '@dynatrace/strato-components/typography';
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, which 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.
To use functions like toBeInTheDocument
, you need to import the @testing-library/jest-dom
package in all your tests. However, you can import it for all tests by default with the setupFilesAfterEnv
, as follows:
{
...
setupFilesAfterEnv: [`<rootDir>/src/jest-setup.ts`]
}
And then import the @testing-library/jest-dom
in jest-setup.ts
file as following:
import '@testing-library/jest-dom';
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.
If you're using styled-components
, you might get an error such as ResizeObserver not found
. To fix it, 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 provides two helper functions, setup
and clear
, for writing tests. To initialize and clear a set of mocks, call these helper functions in a file which is also set in setupFilesAfterEnv
within your Jest configuration file, as follows:
import { setup, clear } from '@dynatrace/strato-components-preview-testing/jest';
import '@testing-library/jest-dom';
beforeAll(() => {
setup();
// your other mock setups
});
afterAll(() => {
clear();
// your other mock clears
});
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 automatically creates 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.
Your unit tests have access to all available APIs in the Dynatrace JavaScript Runtime.
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' }),
});
});
If you have UI and app functions tests, you can use the projects
property to combine UI and app functions configurations. See the projects property in Jest's documentation.