Frontend testing

Libraries

PermaplanT uses the Vitest testing framework.

React Test Renderer, React Testing Library and User Event are available as companion libraries.

Types and organization of tests

We distinguish three different kinds of tests in the frontend of PermaplanT.

  • Render tests
  • DOM tests
  • Logic tests

Each of these test-types may be used for unit testing or integration testing.

All tests related to a component can be found under the same directory in <component name>.test.tsx

Render tests

Render tests make use of React Test Renderer to check if a components layout has unexpectedly changed by transforming it to plain html. Since most components are made up of other sub-components, most tests of this kind are considered to be integration tests.

After a new render test is run for the first time, React Test Renderer will record a snapshot of the selected component and store it in the __snapshots__ subfolder. If the same component renders differently in the future, e.g. because a subcomponent was changed, the test will fail.

To fix the issue, the programmer has to decide if the changes are intentional and then either fix the issue, or regenerate the affected snapshots using:

npm run test -- --updateSnapshot

DOM tests

DOM tests are used to test a components response to user interaction. In the current test setup this is accomplished using a simulated DOM provided by React Testing Library and js-dom. This makes it possible to run these tests even when a browser is not available (e.g. as part of the CI-pipeline).

If possible, User Event should be used to mock user input.

Logic Tests

The remaining tests are used to test our helper functions and in general code that is not tied to the DOM.

Vitest provides plenty of APIs that can be used to mock functions for this purpose.

Mocking API requests should be done with MSW. There is an example of this in src/__test_utils__/msw_handlers/plants.ts. The gist is, MSW intercepts all requests, for which a handler is defined returns the handler's response instead of the backend's.

Testing strategy

Frontend tests should generally follow these principles:

  • Concentrate on writing meaningful tests instead of increasing code coverage for the sake of increasing code coverage.
  • After fixing a bug, add regression tests wherever possible.
  • Focus on testing code that is hard to verify using E2E or manual testing.
  • DOM and Snapshot tests have low priority. They are a nice tool to detect unexpected changes while restyling or reorganizing components, but provide little utility otherwise.

In practice this means that most "CRUD" style components will usually require little to no frontend tests whatsoever. The main utility of frontend tests is to ensure that more complex algorithms and calculations such as those in frontend/features/map_planning/layers/utils/PolygonUtils.ts are implemented correctly. Furthermore, frontend tests are handy to ensure that PermaplanTs state management system is updated as expected. Of course there are plenty more use cases for frontend testing, but the ones presented above should give a general idea.

Optimizing code for testability

Avoid side effects as much as possible. It is generally easiest to test pure functions (i.e. functions without side effects).

Separating UI code (React Components) from code that performs other tasks - such as state mutation - facilitates testing immensely.

Performance tests

Frontend performance must always be viewed from a user centered perspective. To avoid premature optimization always keep in mind that our main target is to ensure that a majority of users don't experience noticeable slowdowns on our targeted hardware during average use cases.

Manual testing as well as user testing is therefore the best strategy for detecting the most relevant performance issues. Automated performance testing, e.g. using Google Lighthouse, should only be used to avoid sudden, unexpected performance degradations or to monitor long term performance trends. Under no circumstances should any performance metric be used as a target.