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'));