Skip to main content

Testing Library

Purpose​

You want to write maintainable tests that give you high confidence that your components are working for your users. There are many open-source testing libraries designed for React and React-like frameworks. The most popular one of them is the testing library that provides friendly and universal APIs to write scalable test cases.

We provides a binding for the testing library called @goji/testing-library, which should work well with Jest and others test runners.

Installation​

For npm users,

npm install @goji/testing-library --save-dev

For yarn users,

yarn add @goji/testing-library --dev

Read the Jest documentation to setup a Jest config file. You can copy these three files, jest.config.js, jestFileMock.js and jestBabelTransform.js from here to your project.

Example​

import React, { useState } from 'react';
import { render, fireEvent } from '@goji/testing-library';
import { Input } from '@goji/core';

const ExampleInput = ({ onSave }: { onSave: (value: string) => void }) => {
const [value, setValue] = useState('');

return (
<Input
testID="example-input"
value={value}
onInput={e => setValue(e.detail.value)}
onConfirm={() => {
onSave(value);
setValue('');
}}
/>
);
};

describe('example input', () => {
it('works', () => {
const onSave = jest.fn();
const wrapper = render(<ExampleInput onSave={onSave} />);
const input = wrapper.getByTestId('example-input');
expect(input).toBeTruthy();

// input
fireEvent.input(input, 'hi');
expect(input.props.value).toBe('hi');

// confirm and then cleanup
fireEvent.confirm(input);
expect(onSave).toBeCalledTimes(1);
expect(onSave).toBeCalledWith('hi');
expect(input.props.value).toBe('');
});
});

API​

render​

render will run the components in NodeJS by React Test Renderer. It mount the components and run their lifecycles as the same way as real Mini Programs.

It returns a wrapper so you can find and enter the specific children element by different queries. In this case getByTestId returns the first element which matches the testID property.

render Options​

Queries​

GojiJS provides several types of queries, all of them are combined from variants and By- queries. They follow the best practices from the testing library. You can find more information here.

Variants:

  • getBy
  • getAllBy
  • queryBy
  • queryAllBy
  • findBy
  • findAllBy

By- queries:

  • ByTestId(testId: string) : matches the testID property
  • ByText(text: string | RegExp | ((text: string, node: ReactTestInstance) => boolean)) : matches element's inner text, for more details see TextMatch
  • ByProp(propKey: string, propValue: string) : matches specific property

We recommend to use testID because it doesn't affect any runtime cost.

Feel free to create an issue if you'd like to append new queries.

debug​

This methods print the elements in console for debug purpose.

const wrapper = render(<Comp />);
// print all elements
wrapper.debug();
// print specific elements
wrapper.debug(wrapper.getByTextId('test'));

demo of debug

within​

If you'd like to restrict your query in specific container, you can use within.

const Comp = () => (
<View testID="view">
<Button>Click me</Button>
</View>
);
const wrapper = render(<Comp />);
const view = wrapper.getByTestId('view');
const buttonInsideView = within(view).getByText('Click me');

fireEvent​

In your test cases, if you'd like to simulates the interactive events on the elements the fireEvent.[event] could help.

For example, to input text into the <Input testID="my-test-id"> element, you can use,

const input = wrapper.getByTestId('my-test-id');
fireEvent.input(input, 'hello, world!');

fireEvent supports these events,

  • tap
  • input
  • confirm

Feel free to create an issue if you'd like to use new events.

act​

The act function has same behavior in React Test Renderer. Usually you should wrap the async component updating in act.

let increase: Function;
const Comp = () => {
const [count, setCount] = useState(0);
increase = () => setCount(count + 1);

return <View>Count: {count}</View>;
};
const wrapper = render(<Comp />);
act(() => {
// update component
increase();
});
expect(wrapper.getByText('Count: 1')).toBeTruthy;

Please note that all fireEvent methods are already wrapped by act so there is no need to wrap again.

waitFor and waitForElement​

waitFor catches the assertion error util it passed, otherwise throw after timeout. It's used for async component tests.

Here is an example,

await waitFor(() => expect(wrapper.getByText('hello')).toBeTruthy());

It equals to,

await waitForElement(() => wrapper.getByText('hello'));