Skip to content

Unit & Integration tests

Kasun Thennakoon edited this page Dec 6, 2021 · 1 revision

Testing Implementation

We run mainly two types of tests

  • Unit/Integration tests
    • Testing Library (React)
  • E2E tests
    • Cypress

Prerequisites

Require NodeJs 14.x or higher, and npm 6.0 or higher.You may use nvm for installing/managing Node and NPM tools.

How to run

Before running either unit tests or integration test, Go to the application root directory where package.json file is located

i:e:

<apim-apps-root>/portals/publisher/

and run

npm ci

command to download all the dependencies.

Unit tests

To run the unit tests, simply execute the npm test command in the application root directory (where the package.json file located).

npm test

is an alias to jest command defined in package.json.

E2E tests

Test files are located in <Product-APIM-Root>/modules/integration/tests-integration/tests-backend/src/test/resources/jest-integration-tests/). Go to the above location and run

npm test

for more information read the integration test README.md file

Code coverage

Run the command

npm run test:coverage

to generate the code coverage for unit tests.Once generated, The coverage reports will be available in <APP_ROOT>/coverage/ directory.

Unit tests

We use Jest framework for managing the unit tests.it also been used as the assertion library as well. For DOM testing, out of the available libraries such as react-testing-library, Enzyme, and React's TestUtils, etc. . . we have mainly used Enzyme.

Test files naming convention is to add test of a component in the same name as the component's file name in the same location. For example if you write a new component name MySampleComponent.jsx then use MySampleComponent.test.jsx as the test file name for that component. Jest will pickup the files for test which are ending with .test.jsx or .test.js

Troubleshooting

Feel free to update this guide , If you able to find better alternatives or if you find anything that is worth adding here

ResolverError: Error downloading https://raw.githubusercontent.com

This could be due to getting throttled out in github public requests, Wait for few seconds(max 1min) and re-run the tests

image

    ResolverError: Error downloading https://raw.githubusercontent.com/wso2/carbon-apimgt/master/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml

      at node_modules/@apidevtools/json-schema-ref-parser/lib/resolvers/http.js:127:16

Error: ENOSPC: System limit for number of file watchers reached (Ubuntu)

Try following command and increase the watch counts

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

For a quick lookup on Jest features


Troubleshooting Old tests (DEPRECATED)


NOTE

Below FAQ's are related to older Enzyme based tests, Just keeping theme here as an archived item, Do not encourage to add or modify any answers below


Mounted wrapper does not contain the expected rendered react elements

This could happen due to failures in mounting child component, Or your test might take longer time to re-render after a state change. In this case , We could do the suggested workaround in here Or this workaround

Wrapper state returns null

In this case, You might probably looking at wrong component instance (element), Use wrapper.debug() to find out which component is wrapped in

setState does not update the rendered output in wrapper

You probably missed some wrapper.update() calls there. This is an asynchronous call, So make sure to use await.

For more info refer this issue

When mounting, not all components get rendered in component hierarchy

If you expect a component to be there in the rendered output but it's not there, That means you probably have check for the component before all the asynchronous calls get succeed (or get executed).

This could mostly happen if you have mocked an API response with a Promise.resolve() and haven't use await on the dom update or haven't flush the pending Promise resolves.

So in this case either you have to use

await new Promise(resolve => setImmediate(resolve));

To wait for all the promises to get resolve (Exhausts all promises queued) ( source: this comment)

Or you need to use await for all the wrapper modification. i:e Event simulations etc.

Error: Uncaught [TypeError: Cannot read property 'getPartialToken' of null]

This is exception is thrown if you haven't mocked the REST API calls. So use Jest.fn() or Jest.mock('path/to/module') to mock the relevant API calls.

Mounting and testing components return by higher order components (HOC)

Most of the components int he Publisher app are decorated by one or more higher order components provided by various libraries, i:e most of the components are wrapped with withStyle higher order components to get the theming support from Material-UI, Similarly we have user withRoute to get routing details from react-router and injectIntl to get internationalization (i18n) support to the components.

for example in Listing.jsx we have used both injectIntl and withStyles higher order functions

export default injectIntl(withStyles(styles)(Listing));

In this case, Enzyme shallow rendering will just render the outer most component , which is intl wrapper and will not actually render the intended Listing component, So we need to unwrap the HOC, before shallow rendering the component.

When doing Enzyme mount, These HOCs would require some values to be there in the context. Do the following as required

  • For React Intl wrapper

    Use the IntlHelper.js If we could not survive with this Util we might need to add this package to get the full support i:e

    import { mountWithIntl } from 'AppTests/Utils/IntlHelper';
    .
    .
    .
    let wrapper = await mountWithIntl(ThemedListing);
  • React router

Use MemoryRouter for wrapping the component, for example in Listing.test.jsx

<MuiThemeProvider theme={createMuiTheme(light)}>
    <MemoryRouter>
        <Listing />
    </MemoryRouter>
</MuiThemeProvider>
  • Material-UI testing

    • If you's test involves inserting, reading , modifying a value in material-ui component, checkout their test implementation for that component in materia-ui git repo. You will be able to get an idea of how to implement testing for your use case using material-ui components

Use unwrap util from material ui test utils to unwrap the withStyle decorations,

import { unwrap } from '@material-ui/core/test-utils';

const UnwrappedMenuButton = unwrap(MenuButton);

When enzyme mounting use actual theme object and MuiThemeProvider component to wrap the target component.Like in the above (intl) example

Clearing , Resting or Restoring mock implementations

If you want to change the mocked function behavior between different test cases within one test description(file), Use mockReset , mockRestore or mockClear accordingly. FOr more info refer this issue