Project: Backstage (App Definition and Development / Application Definition & Image Build)
File: testing.md

id: testing title: Testing with Jest description: Documentation on How to do unit testing with Jest Backstage uses for all our unit testing needs. Jest is a Facebook-built unit testing framework specifically built for React. It follows in the footsteps of other classic Node.js unit testing-related frameworks and libraries like , , and . Running all tests: yarn test Running an individual test (e.g. `MyComponent.test.tsx`): yarn test MyComponent To run both `MyComponent.test.tsx` and `MyControl.test.tsx` suite of tests: yarn test MyCo :::note Note if `console.log`s are not appearing, run only the individual test you are working on. . ::: Tests should be named `[filename].test.ts`, or `[filename].test.tsx` if it contains JSX (as is the case for a lot of React tests, e.g. components). For example, the tests for `Link.tsx` exist in the file `Link.test.tsx`. Jest has its own built-in assertion library with `expect`, so there is no need to `import` a third-party library like some of the older frameworks required. However since assertion libraries simply throw errors, it would be feasible to import a third-party library if you needed (like Chai or ). We use the light-weight to render React components. TODO. The following principles are good guides for determining if you are writing high quality frontend unit tests. No unit test is better than a bad one. Writing a poor unit test: Gives the illusion your code is more secure or reliable than it actually is. Functions equivalent to a bad comment, in that it leads the next developer into erroneous assumptions. Adds to future work by requiring updates to the unit test for irrelevant code changes. A unit test verifies an output matches an expected input. For backend, this would be that when you provide configuration X, then the object responds with Y. For frontend, this would be that when you provide properties X to a component, then the visual functionality responds with Y. A good unit test does not tell the object how it should do its job but should only compare inputs to outputs. Consider a unit test for a form. A good unit test would not test the order of the form fields. Instead, it would verify that the inputs to the form fields lead to a certain backend call when submit is clicked. Unit test quality is directly proportionate to how much code can change without having to touch the unit test. This is often overlooked! A unit test is not a test to verify the code never changes. Poor unit tests are written so that every time you make a tiny change to the code, you have to update the unit test. A good unit test suite allows a lot of flexibility in how the code is written so that future refactoring can occur without having to touch the original unit tests. The ordering of unit tests in a suite should proceed from least specific to most specific. Jest runs all tests in the order in which they are provided, regardless of the depth of `describe()` blocks you provide. We can use this to help us write tests that will help the next developer debug what they broke. The idea here is that if they were to break a unit test, the next developer should be able to tell from the order in which the tests broke what they should do to fix

For example, good unit tests will verify the arguments to a function in a test prior to a test that validates the output. If you do not test this, then simply throwing an error saying that output was incorrect will lead the next developer into thinking they may have broken the entire functionality of the object rather than simply letting them know they had an invalid input. Generally, a unit test should not test exactly how the output appears, it should test that the functionality has an expected general response to an input change. This piggybacks the Scalability Principle and applies primarily to frontend development. As a general guideline, frontends should be flexible enough so that the UX or design can change while touching the least amount of code possible. So for example, a poor unit test would verify the color of a button when it is hovered. This would be a poor unit test, because if you decide to test a slightly different color on the button the unit test will break. A better unit test would verify that the button's CSS classname is assigned properly on hover or test for something completely different. A classic unit test on frontends is verifying a loading indicator displays when a backend request is being made. Here are some things we could test for _when data is loading_: Did the internal `loading` state of the component change? (poor) This is not a great test because it does not actually test that the functionality (displaying a message to the user) actually happens. It also breaks the Blackbox Principle by expecting the internals of the component must work a certain way. This could be a good test on its own right, but it does not actually achieve the goal of verifying that if our input (loading data) occurs, then the output (displaying a message to the user) has happened. Did the text `"Loading!"` appear in the DOM? (better) This is a better test because it validates functionality, but it breaks the Scalability principle. By testing for `'Loading...'` we are linking our test code to the component's message. If we want to add internationalization or simply change the message to something more specific we will break our test and have to update code in two places. Did `<Loading />` get mounted? (best) This is the best test of these examples (there could be more depending on your implementation). Verifying that `<Loading />` is mounted when data is loading is the best test because it fulfills all the principles above: Fulfills Input/Output Principle: Verifies the output changes when the input changes Fulfills Blackbox Principle: Does not verify how the `<Loading />` component is mounted, just that it is mounted in response to the input. Fulfills Scalability Principle: If we decide to refactor the entire way the loading indicator has displayed the test still works without touching it. Fulfills Broken Functionality Principle: this test verifies the functionality (displaying an indicator) is working, rather than how it is working. The increasing complexity principle does not really apply to this example, so it was excluded. However if you were to place this test in a suite of other tests, it would be best to test first that when the component is instructed to load data then it actually does

this way both tests fail if the data loading part breaks and the next developer immediately know the problem is that the data loading is broken, not that the loading indicator is broken. A utility function is a function with no side effects. It takes in arguments and returns a result or displays an error or console message, like so: `StringUtil ellipsis` ```ts export function ellipsis(text, maxLength, midCharIx = 0, ellipsis = '...') { // Do something blackbox. We should not care about the internals, // only inputs and outputs. ... return someFinalValue; } ``` There are four things to test for in a utility function: Handle Invalid Input Verify default input arguments Verify output for expected input arguments Handle thrown errors Handle Invalid Input (handle thrown errors): ```ts it('Throws an error on improper arguments', () => { expect(() => { ellipsis(); }).toThrowError("Expected 'text' to be defined"); }); ``` Verify default input arguments: ```ts it('Works with defaults', () => { expect(ellipsis('Hello world', 3)).toBe('Hel...'); expect(ellipsis('', 3)).toBe(''); expect(ellipsis('H', 3)).toBe('H'); expect(ellipsis('Hello', 5)).toBe('Hello'); }); ``` Verify output for expected input arguments: This is especially true for edge cases! ```ts it('Works with midCharIx', () => { expect(ellipsis('Hello world', 3, 6)).toBe('...o w...'); expect(ellipsis('', 3, 6)).toBe(''); expect(ellipsis('Backstage is amazing', 4, 10)).toBe('...e is...'); }); ``` Testing a JavaScript object which is not a React component follows a lot of the same principles as testing objects in other languages. Testing an API involves verifying four things: Invalid inputs are caught before being sent to the server. Valid inputs translate into a valid browser request. Server response is translated into an expected JavaScript object. Server errors are handled gracefully. involves wrapping existing functions (like an API call function) with an alternative. For example: `./MyApi.ts` ```ts export async function fetchSomethingFromServer() { // Live production call to a URI. Must be avoided during testing! return fetch('blah'); } ``` `./__mocks__/MyApi.ts` ```ts export async function fetchSomethingFromServer() { // Simulate a production call response return 'some result object simulating server data here'; } ``` `./MyApi.test.ts` ```ts // This import will actually return the contents of the file in the // mocks folder now, due to the jest.mock line below import { fetchSomethingFromServer } from './MyApi'; // This instructs Jest to swap all imports of './MyApi.ts' to // './mocks/MyApi.ts' - this gets automatically hoisted to the top // of the file jest.mock('./MyApi'); it('loads data', async () => { await expect(fetchSomethingFromServer()).resolves.toBe( 'some result object simulating server data here', ); }); ``` The is asynchronous. When you call `setState` or update the `props` of a component, there are several asynchronous stages that must occur before a rerender. Note the following example: ```tsx class MyComponent extends Component { load() { this.setState({loading: true}); } render() { return this.state.loading ? <Loading /> : 'Finished!'; } } ... // INCORRECT it('Test loading', () => { const wrapper = mount(<MyComponent />); wrapper.load(); expect(wrapper.find('Loading').length).toEqual(1); // Will fail }); // CORRECT it('Test loading', () => { const wrapper = mount(<MyComponent />); wrapper.load(); wrapper.update(); // This tells the components to run through a render cycle expect(wrapper.find('Loading').length).toEqual(1); }); ``` For more information: The Backstage application has several core providers at its root. To run your test wrapped in a "sample" Backstage application, you can use our utility functions: `wrapInTestApp` import { wrapInTestApp } from '../../test-utils'; ... it('Definitely is not a coconut', () => { const mangoWrapper = mount(wrapInTestApp(<Mango />)); expect(mangoWrapper.context().store).toBeDefined(); }); Note: wrapping in the test application requires you to do a `find()` or `dive()` since the wrapped component is now the application. You can find it