Shubham Ankit

Shubham Ankit

1562208008

Testing Stateful React Function Components with React Testing Library

With the introduction of React Hooks, there is more of an incentive to write function components in React since there is no longer a need use classes when building components.

When it comes to testing React components, there are two popular libraries that are often reached for: enzyme and react-testing-library. I used to always reach for enzyme when testing my react components, but I’ve recently made the switch to react-testing-library because the react-testing-library API encourages tests that are completely ignorant of implementation details. Why is this good? Testing implementation details can lead to both false negatives and false positives, which make tests much more unreliable.

In this post, I’ll look at an example stateful function component that is tested with react-testing-library. I’ll also write the same component into its class component equivalent and show how the class component can be tested with enzyme.

Checklist Example

Here’s a checklist component that allows a user to check off items and display a message after all the items have been checked.

Note: All these examples are written in TypeScript.

export const Checklist = ({ items }: ChecklistProps) => {
    const [checklistItems, setChecklistItems] = useState(items);

    const handleClick = (itemIndex: number) => {
        const toggledItem = { ...checklistItems[itemIndex] };
        toggledItem.completed = !toggledItem.completed;
        setChecklistItems([...checklistItems.slice(0, itemIndex), toggledItem, ...checklistItems.slice(itemIndex + 1)]);
    };

    // Determine if all tasks are completed
    const allTasksCompleted = checklistItems.every(({ completed }) => completed);

    return (
        <div>
            <form>
                {checklistItems.map((item, index) => (
                    <React.Fragment key={item.description}>
                        <input
                            onChange={() => handleClick(index)}
                            type="checkbox"
                            className="checkbox"
                            checked={item.completed ? true : false}
                            id={item.description}
                        />
                        <label htmlFor={item.description}>{item.description}</label>
                    </React.Fragment>
                ))}
            </form>
            <TasksCompletedMessage className="xs-text-4 text-green xs-mt2" visible={allTasksCompleted}>
                All tasks completed{' '}
                <span role="img" aria-label="checkmark">
                    ✅
                </span>
            </TasksCompletedMessage>
        </div>
    );
};

Here’s what the component would look like when used:

Checklist with two unchecked items.

Checklist with two checked items and a message indicating all items are checked.

Now when I’m thinking of testing this component, I want to make sure that a user is able to properly select a checkbox and also display the completed message when all the items have been checked. Here’s how these tests would look like when written with react-testing-library:

afterEach(cleanup);

const mockItems = [
    {
        description: 'first item',
        completed: false,
    },
    {
        description: 'second item',
        completed: false,
    },
    {
        description: 'third item',
        completed: false,
    },
];

describe('Checklist', () => {
    it('should check two out the three checklist items', () => {
        const { getByText, getByLabelText } = render(<Checklist items={mockItems} />);

        fireEvent.click(getByText('first item'));
        fireEvent.click(getByText('second item'));

        expect(getByLabelText('first item').checked).toBe(true);
        expect(getByLabelText('second item').checked).toBe(true);
        expect(getByLabelText('third item').checked).toBe(false);
        expect(getByText('All tasks completed')).not.toBeVisible();
    });

    it('should display a message when all items are completed', () => {
        const { getByText, getByLabelText } = render(<Checklist items={mockItems} />);

        fireEvent.click(getByText('first item'));
        fireEvent.click(getByText('second item'));
        fireEvent.click(getByText('third item'));

        expect(getByLabelText('first item').checked).toBe(true);
        expect(getByLabelText('second item').checked).toBe(true);
        expect(getByLabelText('third item').checked).toBe(true);
        expect(getByText('All tasks completed')).toBeVisible();
    });
});

There are a few special things of note in these tests. The first being how we are targeting elements on the page by their text rather than by a class name, id, or other DOM selector. This is important because this is actually how a user will find an element on the page. A user doesn’t see or care about what classes or ids are found on an element so it’s unrealistic to expect a user to find and interact with an element on a page based on a DOM selector.

react-testing-library doesn’t only allow you to target elements by text, but you can also target elements through labels, placeholder text, alt text, title, display value, role, and test id (see the documentation for details on each of these methods of targeting elements).

Another important thing to notice in these tests is that we aren’t looking at the value for the internal component state, nor are we testing any of the functions being used within the component itself. Basically what this means is that we don’t care about testing the implementation details of our component, but we are more interested in testing how the component will actually be used by a user. Actually, it’s extremely difficult to test implementation details of a function component since it’s not possible to access the component state, nor can we access any of the functions/methods that are defined and used inside of the component. However, as a fun exercise, let’s look at our checklist component written in as a class component:

export class Checklist extends React.Component<ChecklistProps, ChecklistState> {
    state = {
        checklistItems: this.props.items,
    };

    handleChange = (itemIndex: number) => {
        const toggledItem = { ...this.state.checklistItems[itemIndex] };
        toggledItem.completed = !toggledItem.completed;
        this.setState({
            checklistItems: [
                ...this.state.checklistItems.slice(0, itemIndex),
                toggledItem,
                ...this.state.checklistItems.slice(itemIndex + 1),
            ],
        });
    };

    render() {
        // Determine if all tasks are completed
        const allTasksCompleted = this.state.checklistItems.every(({ completed }) => completed);
        return (
            <div>
                <form>
                    {this.state.checklistItems.map((item, index) => (
                        <React.Fragment key={item.description}>
                            <input
                                onChange={() => this.handleChange(index)}
                                type="checkbox"
                                className="checkbox"
                                checked={item.completed ? true : false}
                                id={item.description}
                            />
                            <label htmlFor={item.description}>{item.description}</label>
                        </React.Fragment>
                    ))}
                </form>
                <TasksCompletedMessage className="xs-text-4 text-green xs-mt2" visible={allTasksCompleted}>
                    All tasks completed{' '}
                    <span role="img" aria-label="checkmark">
                        ✅
                    </span>
                </TasksCompletedMessage>
            </div>
        );
    }
}

Now, let’s use enzyme to test our checklist class component. However, this time we will be testing the implementation details of our component.

const mockItems = [
    {
        description: 'first item',
        completed: false,
    },
    {
        description: 'second item',
        completed: false,
    },
    {
        description: 'third item',
        completed: false,
    },
];

describe('Checklist Class Component', () => {
    it('should render all 3 list items', () => {
        const wrapper = mount(<Checklist items={mockItems} />);

        expect(wrapper.find('label').length).toBe(3);
    });

    describe('handleChange', () => {
        it('should check two out the three checklist items', () => {
            const wrapper = mount(<Checklist items={mockItems} />);
            const instance = wrapper.instance();

            instance.handleChange(0);
            instance.handleChange(1);

            expect(wrapper.state('checklistItems')).toEqual([
                {
                    description: 'first item',
                    completed: true,
                },
                {
                    description: 'second item',
                    completed: true,
                },
                {
                    description: 'third item',
                    completed: false,
                },
            ]);
        });

        it('should display a message when all items are completed', () => {
            const wrapper = mount(<Checklist items={mockItems} />);
            const instance = wrapper.instance();

            instance.handleChange(0);
            instance.handleChange(1);
            instance.handleChange(2);
            wrapper.update();

            expect(
                wrapper
                    .find('.text-green')
                    .first()
                    .props().visible,
            ).toBe(true);
        });
    });
});

Because the enzyme API makes available a component’s state as well as the class methods of component (by accessing the component’s instance), we are now able to test both those things. For example, looking at the test labelled should check two out the three checklist items, the handleChange method is triggered twice (which should happen when a user clicks two checklist items) and then the value of the state is checked to make sure it has updated appropriately. The problem with this test is that we aren’t testing how this component is actually being used. The user doesn’t care about the value of a component’s internal state or if a function has been called. All a user cares about (in this case) is that they are able to click on two checklist items and that both those checklist items appear as checked to them.

Enzyme’s API doesn’t allow for an element to be find by it’s text, it only allows for elements to be selected based on a CSS selector, React component constructor, React component display name, or based on a component’s props (see here for details on Enzyme selectors). Because Enzyme’s API basically pushes you to test implementation details for a component, I prefer to stay away from Enzyme and instead use react-testing-library.

Refactoring Class Components to Function Components

Another advantage of using react-testing-library and not testing for implementation details is that you can easily refactor your class component to a function component without having the also refactor your tests. Think about it, if you’re targeting class methods in your tests, those methods will no longer be available when it’s being implemented within a function component.

Demo Repository

I’ve setup a demo repository, that contains the above example with the checklist and I’ve also created another example for a component named SelectTwo, which is a list of items that only allows for 2 items to be selected at once.

View the repo

Other Ressources

Here are some great ressources that you should check out if you’re interested in learning more about react-testing-library.

#reactjs #javascript

What is GEEK

Buddha Community

Testing Stateful React Function Components with React Testing Library
Autumn  Blick

Autumn Blick

1598839687

How native is React Native? | React Native vs Native App Development

If you are undertaking a mobile app development for your start-up or enterprise, you are likely wondering whether to use React Native. As a popular development framework, React Native helps you to develop near-native mobile apps. However, you are probably also wondering how close you can get to a native app by using React Native. How native is React Native?

In the article, we discuss the similarities between native mobile development and development using React Native. We also touch upon where they differ and how to bridge the gaps. Read on.

A brief introduction to React Native

Let’s briefly set the context first. We will briefly touch upon what React Native is and how it differs from earlier hybrid frameworks.

React Native is a popular JavaScript framework that Facebook has created. You can use this open-source framework to code natively rendering Android and iOS mobile apps. You can use it to develop web apps too.

Facebook has developed React Native based on React, its JavaScript library. The first release of React Native came in March 2015. At the time of writing this article, the latest stable release of React Native is 0.62.0, and it was released in March 2020.

Although relatively new, React Native has acquired a high degree of popularity. The “Stack Overflow Developer Survey 2019” report identifies it as the 8th most loved framework. Facebook, Walmart, and Bloomberg are some of the top companies that use React Native.

The popularity of React Native comes from its advantages. Some of its advantages are as follows:

  • Performance: It delivers optimal performance.
  • Cross-platform development: You can develop both Android and iOS apps with it. The reuse of code expedites development and reduces costs.
  • UI design: React Native enables you to design simple and responsive UI for your mobile app.
  • 3rd party plugins: This framework supports 3rd party plugins.
  • Developer community: A vibrant community of developers support React Native.

Why React Native is fundamentally different from earlier hybrid frameworks

Are you wondering whether React Native is just another of those hybrid frameworks like Ionic or Cordova? It’s not! React Native is fundamentally different from these earlier hybrid frameworks.

React Native is very close to native. Consider the following aspects as described on the React Native website:

  • Access to many native platforms features: The primitives of React Native render to native platform UI. This means that your React Native app will use many native platform APIs as native apps would do.
  • Near-native user experience: React Native provides several native components, and these are platform agnostic.
  • The ease of accessing native APIs: React Native uses a declarative UI paradigm. This enables React Native to interact easily with native platform APIs since React Native wraps existing native code.

Due to these factors, React Native offers many more advantages compared to those earlier hybrid frameworks. We now review them.

#android app #frontend #ios app #mobile app development #benefits of react native #is react native good for mobile app development #native vs #pros and cons of react native #react mobile development #react native development #react native experience #react native framework #react native ios vs android #react native pros and cons #react native vs android #react native vs native #react native vs native performance #react vs native #why react native #why use react native

Chumarat Pat

Chumarat Pat

1599639298

Interaction Testing with React Testing Library

Testing is complicated. I’ve certainly never been good at it. For the longest time, I’ve only been focused on basic function input-output unit tests. Why? Because they were easy — you didn’t need to render HTML, you didn’t need to query DOM elements, you didn’t need to interact with said DOM elements. But of course, React component testing is a necessity for any mature codebase. And it finally came time for me to sit down and figure it out.

That’s when I discovered React Testing Library. And suddenly, everything seemingly became much simpler. All the complexities that I’ve encountered, but not understood, that made me put off React component testing disappeared. Hopefully, the same will happen for you.

#react-testing-library #unit-testing #react #jest #interaction-testing

Alayna  Rippin

Alayna Rippin

1597168800

Test Driven Development (TDD) with React Testing Library & Mock Service Worker

Let’s step up our testing game with two useful libraries that lend themselves excellently to a TDD approach.

Setting up

Whenever I want to try out something React-related, I use the library create-react-app. It gives you a ready-to-work-with basic React application with no configuration needed. Recent versions also come bundled with React Testing Library, so if you use the latest create-react-app, you can start using React Testing Library straight away. If not — install @testing-library/react and @testing-library/jest-dom in your existing React application.

I am going to implement the following functionality: a simple recipe list with a search function. It will look something like this to a user in a mobile browser:

Image for post

Image credits: Burger Photo by Robin Stickel on Unsplash, French Toast Photo by Joseph Gonzalez on Unsplash, Salmon Photo by Casey Lee on Unsplash

Starting with a failing test

I want to use a TDD approach, so let’s start with a failing test. At this point, there is no component yet, so of course whatever test we write is going to fail. But let’s start small; I want a component named ‘Recipes’ that renders the expected heading text. Here is my test for that expectation:

	import React from 'react';
	import { render, screen } from '@testing-library/react';

	test('renders the heading', () => {
	  render(<Recipes />);

	  expect(screen.getByRole('heading')).toHaveTextContent('Recipe Finder');
	});

React testing library exports a render method, which will render a component and all of its child components. It also exports a screen object, holding a number of queries we can use to select different elements in our rendered component (and its child components too). The **getByRole **query lets me select the heading element and make an assertion on its text content.

Why select the heading element by its role and not for example a CSS class? The guiding principle of React Testing Library is “The more your tests resemble the way your software is used, the more confidence they can give you.” Therefore, we want to write our tests as close as possible to how the ultimate tester — the end user — will be using the application. Users don’t see CSS classes or data attributes; they interact with text, label text and semantic elements and roles. Using queries such as getByRole also encourages us to write accessible code, since these selectors are available to everyone, including users of screen readers.

Making the test pass

Our first test fails as expected.** Recipes is not defined.** But this is the first step in TDD — a failing test. Now, let’s make it pass by writing the simplest possible component with the correct heading and then importing it in our test file. Now, let’s re-run the test. It passes!

	import React from 'react';

	const Recipes = () => {
	  return (
	    <div>
	      <h1>Recipe Finder</h1>
	    </div>
	  )
	};

	export default Recipes;

Using further queries, we can make similar expectations for the input element and the “Find” button. The button also has a role, but for the input field, I will use the getByPlaceholderText query, since that is probably the closest query to how the user would find it on the page.

Start with a failing test…

	import React from 'react';
	import { render, screen } from '@testing-library/react';
	import Recipes from './Recipes';

	test('renders the heading, input field and button', () => {
	  render(<Recipes />);

	  expect(screen.getByRole('heading')).toHaveTextContent('Recipe Finder');
	  expect(screen.getByPlaceholderText('Enter an ingredient to find recipes...'))
	    .toBeInTheDocument();
	  expect(screen.getByRole('button')).toHaveTextContent('Find');
	});

… and implement the changes necessary to make it pass:

	import React from 'react';

	const Recipes = () => {
	  return (
	    <div>
	      <h1>Recipe Finder</h1>
	      <form>
	        <input 
	          type="text" 
	          name="ingredient"
	          placeholder="Enter an ingredient to find recipes..." 
	        />
	        <button type="submit">Find</button>
	      </form>
	    </div>
	  )
	};

	export default Recipes;

This way, we know what we expect from our code and more importantly — we will know if we break any functionality if the previously passing tests suddenly fail.

An important step in TDD is the refactor step, where we improve our code to for example make it easier to read, become more efficient and remove any duplication. The test should still pass after we refactor.

Setting up our mocks

When the application first renders, I want to display a list of all my recipes, just like in the visual design above. This requires some kind of communication with an API. We are going to use Mock Service Worker to mock the HTTP-requests, so that we can control the response data. Install Mock Service Worker with npm like this:

npm install msw --save-dev

With Mock Service Worker, we are not mocking a specific module (unlike if we were to use Jest.mock), which means that it makes no difference if I use fetch or a third-party library such as axios to get the data. This makes it incredibly flexible. Let’s add the following imports to our test file:

import { rest } from 'msw';
import { setupServer } from 'msw/node';

Here is how I set up mocking a call to the recipe list endpoint:

	import React from 'react';
	import { render, screen } from '@testing-library/react';
	import Recipes from './Recipes';
	import { rest } from 'msw';
	import { setupServer } from 'msw/node';

	const allRecipes = [
	  { id: 1, title: 'Burger' }, 
	  { id: 2, title: 'French toast' }, 
	  { id: 3, title: 'Salmon' }
	];

	const server = setupServer(
	  rest.get('/api/recipes', (req, res, ctx) => {
	    return res(ctx.json({ recipes: allRecipes }));
	  })
	);

	beforeAll(() => server.listen());

afterAll(() => server.close());

If you have worked with NodeJS and Express, the syntax looks very familiar. GET requests to ‘/api/recipes’ will respond with JSON containing the allRecipes array, just like a real API would. These two lines make sure the server starts listening (intercepting) before the tests run and closes its connection when the tests in this file have finished running:

beforeAll(() => server.listen());
afterAll(() => server.close());

#tdd #react-testing-library #react #test-driven-development #testing

Jeremy  Reilly

Jeremy Reilly

1603955580

Simulate Browser Interactions with Testing Library’s UserEvent

My Journey

Like most, when I first started using Testing LibraryI used Fire Event to test component interactions. After all, this API shipped with the library itself and was used in the test examples in the documentation. But I soon discovered that Fire Event had serious limitations. I would try clicking something and the expected effect did not happen. Why?

Browser Events

To understand this issue, we need to better understand browser events. When a user clicks something in their browser, multiple events are triggered — mouseDownmouseUpclick, and focus. Similarly, when typing something, the keyDownkeyUp, and keyPress events all trigger! Because a single user interaction could trigger multiple events, developers have multiple options for implementation. This is where I ran into my issue.

Fire Event

Fire Event, unfortunately, requires you to use the method for the corresponding event handler to trigger. If an element has an onClick event handler, I have to use fireEvent.click; if an element has an onMouseDown event handler, I have to use fireEvent.mouseDown. In other words, I need to know the exact implementation of the event handler to successfully use fireEvent.

#react #jest #integration-testing #unit-testing #react-testing-library #react native

Dedric  Reinger

Dedric Reinger

1599224220

React Testing Library: The Modern Way to Test React Components

Unit testing, and in our case, testing components, is a key element of any scalable and maintainable project. That truism is even more obvious now in the age of independent and shareable components. When sharing components between projects, testing serves as a way to both validate that a component behaves as expected and that it is independent and reusable (otherwise it would show to be very difficult to test).

Image for post

React components shared from different projects to Bit.dev (using Bit)

When it comes to testing React components, one of the most popular testing frameworks is Enzyme. But if you start a brand new project using thecreate-react-appcommand-line tool, what you would see is the React Testing Library (RTL). It’s not just another testing utility. This library promotes a different way of testing React components. If we take a closer look, it makes it much easier to test these components.

1. The challenge with Enzyme

Let’s have a look a the challenges with the current testing approach using a practical example.

Just imagine, you write your unit tests based on CSS class selectors. In a later stage, a refactoring happens, and bam !!! all your tests are failing now.

The same thing happens when you depend on props and state in your tests. Here the tests are more tightly coupled with implementation details. Hence, it becomes less maintainable.

#react-testing-library #web-development #react #javascript #front-end-development