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
In your app, you'd want to test the UI components. 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
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
: It's a Jest environment inside which 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.
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:
{
"compilerOptions": {
...
"types": [
...,
"@types/jest", "@testing-library/jest-dom"
],
},
}
Create config
You write Dynatrace Apps in TypeScript, which Jest can't understand by default. And Dynatrace Apps use the Strato design system, which requires some custom config to be testable. To support both, you'll need to create create a jest.config.ts
file in the root directory with the following code:
import type { Config } from '@jest/types';
import { stratoPreset } from '@dynatrace/strato-components-preview/testing/jest';
const config: Config.InitialOptions = {
preset: 'ts-jest',
displayName: 'ui',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src'],
setupFiles: ['@dynatrace/strato-components-preview/testing'],
...stratoPreset,
};
export default config;
Let's understand the config.
- preset: This property points to the npm module with a Jest preset, which you can use as a base for Jest's configuration. In this case, you'll need
ts-jest
to test the TypeScript code. - 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. - stratoPreset: It's a set of default configurations required if you're using
Strato
components. It also usesmoduleNameMappers
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:
{
...
transform: {
'.(css|scss|sass|less)$': '<rootDir>/style-mock.ts'
},
}
Add the following content in 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:
// imports
import React from 'react';
import { render, screen } from '@dynatrace/strato-components-preview/testing';
import '@testing-library/jest-dom/extend-expect';
// 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
method from the Strato design system that allows 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.
You'd need to import @testing-library/jest-dom/extend-expect
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:
{
...
setupFilesAfterEnv: [`<rootDir>/src/jest-setup.ts`]
}
And then import the @testing-library/jest-dom/extend-expect
in jest-setup.ts
file as following:
import '@testing-library/jest-dom/extend-expect';
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.
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/extend-expect';
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
function, 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();
Only use the setup
and clear
functions if the Strato components don't behave as they should.
Test your app functions
The Dynatrace App Toolkit automatically creates a test file for every function you generate by running the generate function
command. You also get the needed 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 is already containing 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 configurations for both UI and app functions. Take a look at the projects property