Unit Testing Documentation - Swent-Fall-2024-team-10/leazy GitHub Wiki
We should not use react-test-renderer or @types/react-test-renderer because it is deprecated, as mentioned here :
As recommended by React-team, we should use :
If at some points test examples that you would like to reproduce use one of the deprecated libraries you will have to make some changes by "renderer" calls to a call render
from the '@testing-library/react-native'
library.
Let's take this example from Jest which uses a deprecated library and fix it:
import React from 'react';
import renderer from 'react-test-renderer';
import Intro from '../Intro';
test('renders correctly', () => {
// They use renderer.create !! We can't do that because we don't use this library
const tree = renderer.create(<Intro />).toJSON();
expect(tree).toMatchSnapshot();
});
Instead, you should write :
import React from 'react';
// Change the import
import { render } from '@testing-library/react-native';
import Intro from '../Intro';
test('renders correctly', () => {
// renderer.create to render()
const tree = render(<Intro />).toJSON();
expect(tree).toMatchSnapshot();
});
And you should have a test that now run using the testing library of react-native instead of a deprecated one.
You can find this example in the test_example
folder.
The organization of the files for the unit testing should be similar to the below example :
project-example-root/
├── components/
│ ├── ThemedText.tsx
│ └── __tests__/
│ └── ThemedText-test.tsx
└── utils/
├── index.tsx
└── __tests__/
└── index-test.tsx
The test directory should be __tests__
, and the name of the test files should follow this format : <name_of_tested_element>-test.tsx
The example directory tree above gives an example of the file name format
For this example we'll use a basic component and we'll show the basic syntax of the testing library:
import React from 'react'
import { View, Text} from 'react-native'
interface GreetingProps {
name : String
isMorning : Boolean
}
export default function Greeting({name, isMorning} : GreetingProps) {
return (
<View>
<Text> {isMorning ? 'Good Morning' : 'Good Afternoon'} {name}! </Text>
</View>
)
}
This example should create a component that display a greeting to the user and should be different depending on the time of the day.
Now we write the tests:
Since there are multiple results we would like to test, we will pack those in a test suite using describe()
from the Jest library :
// This is similar to the way we test components without props
describe("<Greeting />", () => {
test("Greeting renders correctly on the morning", () => {
const { getByText } = render(<Greeting name="Adrien" isMorning={true} />);
getByText("Good Morning, Adrien!");
});
test("Greeting renders correctly on the afternoon", () => {
const { getByText } = render(<Greeting name="Adrien" isMorning={false} />);
getByText("Good Afternoon, Adrien!");
});
});
Note:
- that we could have created single tests by removing the call to
describe
if we wanted. But since we're testing the same functionality of one component, it makes sense to group them together. - If we had no props, it would be the same syntax but we would remove the props from the components, there is an easy example in the
/tests_examples
directory.
First we create a simple component that will be a button that increment a counter each time it is clicked.
import React from 'react'
import {View, Text, Button} from 'react-native'
import { useState } from 'react'
interface SimpleCounterProps {
baseValue : number
}
// This component is a simple counter that increments the count when the button is pressed
export default function SimpleCounter({baseValue}: SimpleCounterProps) {
const [count, setCount] = useState(baseValue)
return (
<View>
<Text>{count}</Text>
<Button title="Increment" onPress={() => setCount(count + 1)} />
</View>
)
}
Then similarly as with the other tests, we create a test suite using describe()
, and in it we define the tests.
Now we use the fireEvent.press()
to simulate a click on the button and then we just have to check that the value of the component is indeed the incremented counter
import React from 'react'
import { render, fireEvent } from '@testing-library/react-native'
import SimpleCounter from '../ExampleWithState'
// This simple test shows how to test a component that uses the useState hook
// with the example of a button that increments a counter
describe('SimpleCounter', () => {
test('base value should be the argument', () => {
const { getByText } = render(<SimpleCounter baseValue={2} />)
expect(getByText('2')).toBeTruthy()
})
test('increment button should increment the count', () => {
const { getByText } = render(<SimpleCounter baseValue={0} />)
expect(getByText('0')).toBeTruthy()
fireEvent.press(getByText('Increment'))
expect(getByText('1')).toBeTruthy()
for (let i = 0; i < 5; i++) {
fireEvent.press(getByText('Increment'))
}
expect(getByText('6')).toBeTruthy()
})
})
Snapshot testing with Jest is a way to do regression testing and catch unwanted changes, this kind of tests are good if you have a component that should not change much, because otherwise you will need to update the snapshot test regularly.
- On the first run of your snapshot test, the component will be converted into a string format and saved in a snapshot folder it is a representation of the rendered component with all the relevant structure and attributes.
- In subsequent test run, the current output will be compared with the snapshot.
You can execute your tests in the terminal by running the command :
$npm run test
This command not only runs the tests but also generates a code coverage report. Since Jest is configured to operate in --watchAll mode, it will automatically run all tests whenever changes are detected. This feature allows you to see test results in real-time, eliminating the need to manually rerun the command after each change.