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 thetests
folder. - Unit tests should be written in behavior-driven development (BDD) style. For example:
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');
})
})
})
- Experience Builder uses Jest as the testing framework.
- Experience Builder uses @testing-library/react to test React components.
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
wrap
andWidget wrap
that are exported fromWidget Setting 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
get
andInit State() get
to init store like this:Default App Config()
getAppStore().dispatch(appActions.updateStoreState(getInitState().merge({
appConfig: getDefaultAppConfig().merge({
widgets: {
w1: {
label: 'W 1'
}
}
})
})))
- To mock a feature service, you can call
mock
with your mock data like this:Feature Layer
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:
const {queryByText} = render(<Component/>)
const {queryByText} = withStoreRender(<Component/>)
Then check the render result like this:
expect(queryByText('the text')).toBeInTheDocument();
- To render a widget, you should:
// 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 load
, you can mock it like this:
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.