Unit Testing

General principles for unit tests

The key to good unit testing is to write testable code. Applying simple design principles can help. In particular:

  • Use a good naming convention and comment your code (the "why?" not the "how"). Keep in mind that comments are not a substitute for bad naming or bad design.
  • DRY: Don't Repeat Yourself - avoid code duplication.
  • Single responsibility: each object/function must focus on a single task.
  • Keep a single level of abstraction in the same component. For example, do not mix business logic with lower-level technical details in the same method.
  • Minimize dependencies between components: encapsulate and interchange less information between components.
  • Support configurability rather than hard-coding. Doing this prevents having to replicate the exact same environment when testing.

Unit tests in ArcGIS Experience Builder

  • Unit tests files must end with .test.ts(x) or .spec.ts(x) and should be put in the tests folder.
  • Unit tests should be written in behavior-driven development (BDD) style. For example:
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
describe('calculator', function() {
  // Describes a module with nested "describe" functions
  describe('add', function() {
    // Specify the expected behavior
    it('should add 2 numbers', function() {
      // Use assertion functions to test the expected behavior
      expect(foo).toBeTruthy();
      expect(foo).toBe('bar');
    })
  })
})

Write unit tests in Experience Builder

In general, you may need to write unit tests for these three kinds of code:

  • Native TypeScript code: For this kind of code, you may be able to use Jest only. In most cases, you need to mock some components to make it easier to write the unit tests.
  • Native React component: For this kind of code, you should use Jest and @testing-library/react. Use Jest as a testing framework, and use the testing library to render react components then check the render result.
  • Experience Builder widget: For an Experience Builder widget, you should use wrapWidget and wrapWidgetSetting that are exported from jimu-for-test.

For all of the above kinds of code, jimu-for-test provides some helper utility functions to write tests easily. For example:

  • Init state: you can use getInitState() and getDefaultAppConfig() to init store like this:
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
  getAppStore().dispatch(appActions.updateStoreState(getInitState().merge({
    appConfig: getDefaultAppConfig().merge({
      widgets: {
        w1: {
          label: 'W 1'
        }
      }
    })
  })))
  • To mock a feature service, you can call mockFeatureLayer with your mock data like this:
Use dark colors for code blocksCopy
1
mockFeatureLayer(mockData)

After the feature layer is mocked, the request in the unit test will not make a network request, making the test more stable and fast.

  • To render a React component, you can render it like this:
Use dark colors for code blocksCopy
1
2
const {queryByText} = render(<Component/>)
const {queryByText} = withStoreRender(<Component/>)

Then check the render result like this:

Use dark colors for code blocksCopy
1
expect(queryByText('the text')).toBeInTheDocument();
  • To render a widget, you should:
Use dark colors for code blocksCopy
1
2
3
4
5
6
// Get the widget render, you can pass in store/theme/locale optionally
const render = widgetRender();
// Wrap the widget to inject some props into widget component
const Widget = wrapWidget(_Widget, {config: {}});
// Then, you can render the widget like a native react component
const {queryByText} = render(<Widget/>)

Test modules that use the ArcGIS Maps SDK for JavaScript

You should mock the modules you used in your component. If you load the modules through loadArcGISJSAPIModule, you can mock it like this:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jest.mock('jimu-core', () => {
  return {
    ...jest.requireActual('jimu-core'),
    loadArcGISJSAPIModule: jest.fn().mockImplementation(moduleId => {
      let module
      if(moduleId === 'esri/layers/FeatureLayer'){
        module = jest.fn().mockImplementation(() => {
          return {
            queryFeatureCount: () => Promise.resolve(5)
          }
        })
      }
      return Promise.resolve(module)
    })
  }
})

If you import the modules, you can use jest.mock().

Run unit tests

In the client folder, run npm test.

See the show-unit-tests sample for an example set of unit tests.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.