I'm building a React Native application with TypeScript. I'm using React Native Testing Library for my component tests.
I have a simple component that renders two clickable icons and a text. It's a counter that can increment and decrement the number.
import React, { PureComponent } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import { Button, Icon } from "react-native-elements";
import { getIconName } from "../../services/core";
import styles from "./styles";
export interface AmountButtonProps {
amount: number;
onDecrement: () => void;
onIncrement: () => void;
size: "small" | "large";
}
export class AmountButtons extends PureComponent<AmountButtonProps> {
render() {
const { amount, onDecrement, onIncrement, size } = this.props;
const fontSize = size === "small" ? 14 : 26;
const minusDisabled = amount <= 1;
const plusDisabled = amount >= 25;
return (
<View style={styles.container}>
<Icon
containerStyle={[
styles[size],
styles.iconContainer,
styles.minusIcon,
minusDisabled && styles.disabled
]}
onPress={onDecrement}
type="ionicon"
name={getIconName("remove")}
disabled={minusDisabled}
disabledStyle={[styles.iconDisabled, styles.disabled]}
size={fontSize}
component={TouchableOpacity}
/>
<View style={[styles[size], styles.amountContainer, styles.iconContainer]}>
<Text style={{ fontSize }}>{amount}</Text>
</View>
<Icon
containerStyle={[
styles[size],
styles.iconContainer,
styles.addIcon,
plusDisabled && styles.disabled
]}
onPress={onIncrement}
type="ionicon"
name={getIconName("add")}
disabled={plusDisabled}
disabledStyle={styles.iconDisabled}
color="white"
size={fontSize}
component={TouchableOpacity}
/>
</View>
);
}
}
export default AmountButtons;
I wanted to write a simple unit test to see if the user can see the amount. Here is what I wrote.
import React from "react";
import { debug, fireEvent, render } from "react-native-testing-library";
import { getIconName } from "../../services/core";
import AmountButtons, { AmountButtonProps } from "./AmountButtons";
const createTestProps = (props?: object): AmountButtonProps => ({
amount: 1,
onDecrement: jest.fn(),
onIncrement: jest.fn(),
size: "large",
...props
});
describe("AmountButtons", () => {
const props = createTestProps();
const { getByText, getByProps } = render(<AmountButtons {...props} />);
it("displays the amount", () => {
debug(<AmountButtons {...props} />);
expect(getByText(props.amount.toString())).toBeDefined();
});
});
The problem is this test throws the error:
● AmountButtons › displays the amount
Component not found.
18 | it("displays the amount", () => {
19 | debug(<AmountButtons {...props} />);
> 20 | expect(getByText(props.amount.toString())).toBeDefined();
| ^
21 | });
22 |
23 | it("calls onIncrement", () => {
at Object.it (app/components/AmountButtons/AmountButtons.test.tsx:20:12)
Even though in the output of debug
I can see the amount being rendered:
...
}
>
<Text
style={
Object {
"fontSize": 26,
}
}
>
1
</Text>
</View>
<Themed.Icon
...
What is going on here? Why does React Testing Library not see this text? How can I test this?
You can not do that with RTL. You are not supposed to interact with the internals of your components.
Here's how you can do it: // highlight-start jest. mock("react-select", () => ({ options, value, onChange }) => { function handleChange(event) { const option = options. find( (option) => option.
The problem is that rendering your component with RTL's render
method does not happen in sync with the test case. So when the it
block runs you can't be sure that this line of code
const { getByText, getByProps } = render(<AmountButtons {...props} />);
has run and getByText
is bound properly.
In order to solve this you can:
it
block:describe("AmountButtons", () => {
const props = createTestProps();
it("displays the amount", () => {
const { getByText, getByProps } = render(<AmountButtons {...props} />);
expect(getByText(props.amount.toString())).toBeDefined();
});
});
beforeEach
/before
block:describe("AmountButtons", () => {
const props = createTestProps();
let getByText, getByProps;
beforeEach(() => {
({ getByText, getByProps }) = render(<AmountButtons {...props} />);
})
it("displays the amount", () => {
expect(getByText(props.amount.toString())).toBeDefined();
});
});
but in this case you have to keep in let
variables all the getBy
helpers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With