如何测试与测试库的路由器重定向React

React testing-library is very convenient to test React components rendering from props, fire events and check DOM elements. react-router uses a <Redirect> component to trigger a redirect, but how can we test that this component is called using testing-library?

React testing-library非常方便地测试从props,fire事件和检查DOM元素渲染的React组件。 react-router使用<Redirect>组件触发重定向,但是如何使用testing-library测试该组件是否被调用?

Let’s say we have a CreateBookForm component that creates a new book. It calls our API when the form is submitted.

假设我们有一个CreateBookForm组件可以创建一本新书。 提交表单后,它将调用我们的API。

// BookCreateForm.js
import React, { useState } from 'react';
import api from './api';


function CreateBookForm() {
  const [title, setTitle] = useState('');


  async function handleSubmit(event) {
    event.preventDefault();
    await api.createBook({ title });
  }


  return (
    <form onSubmit={handleSubmit}>
      <input
        placeholder="Book's title"
        value={title}
        onChange={(event) => setTitle(event.target.value)}
      />
      <button>Create book</button>
    </form>
  );
}


export default CreateBookForm;

It’s easy to test that our api is called when the form is submitted with testing-library:

使用test-library提交表单时,很容易测试是否调用了我们的api:

// BookCreateForm.test.js
import React from 'react';
import { render, act, fireEvent, waitFor } from '@testing-library/react';


import BookCreateForm from './BookCreateForm';
import api from './api';


jest.mock('./api');


test('it calls api on form submit', async () => {
  api.createBook = jest.fn(() => Promise.resolve({ id: 1 }));
  const { 
    getByPlaceholderText, getByText, findByDisplayValue 
  } = render(<BookCreateForm />);


  await act(async () => {
    const input = getByPlaceholderText(/Book's title/);
    fireEvent.change(input, { target: { value: 'Yama Loka Terminus' }});
    await findByDisplayValue(/Yama Loka Terminus/);


    const button = getByText(/Create book/);
    fireEvent.click(button);
  });


  expect(api.createBook).toHaveBeenCalledWith({ title: 'Yama Loka Terminus' });
});

Now, let’s say we want our component to redirect to the new book page once it’s created.

现在,假设我们希望我们的组件在创建后重新定向到新书页面。

// BookCreateForm.js
import React, { useState } from 'react';
import { Redirect } from 'react-router-dom'


import api from './api';


function CreateBookForm() {
  const [title, setTitle] = useState('');
  const [createdId, setCreatedId] = useState(null);


  async function handleSubmit(event) {
    event.preventDefault();
    const { id } = await api.createBook({ title });
    setCreatedId(id);
  }


  return createdId ?
    <Redirect to={`/book/${createdId}`}/> :
    (
      <form onSubmit={handleSubmit}>
        <input
          placeholder="Book's title"
          value={title}
          onChange={(event) => setTitle(event.target.value)}
        />
        <button>Create book</button>
      </form>
    );
}


export default CreateBookForm;

We’ll probably have a router wrapping our form and a BookPage component:

我们可能会有一个路由器来包装我们的表单和一个BookPage组件:

// App.js
function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Route path="/book/create">
          <BookCreateForm />
        </Route>
        <Route path="/book/:id">
          <BookPage />
        </Route>
      </BrowserRouter>
    </div>
  );
}

Now, our test runner will complain that we use `<Redirect>` outside of a router, so let’s wrap our component test into one.

现在,我们的测试运行者会抱怨我们在路由器外部使用了<Redirect>,因此让我们将组件测试包装为一个。

// BookCreateForm.test.js
// …
import { BrowserRouter } from 'react-router-dom';
// …
  const { 
    container, 
    getByPlaceholderText, 
    getByText, 
    findByDisplayValue 
  } = render(<BrowserRouter><BookCreateForm /></BrowserRouter>);
// …

Everything is working fine, but how can we ensure that our form component is redirecting to the new page after the api’s response?

一切工作正常,但如何确保api响应后表单组件重定向到新页面?

That’s a tricky question and I’ve been struggling with this. I’ve seen some complex solutions involving creating fake routers or mocking the react-router module. But there’s actually a pretty simple way to test this.

这是一个棘手的问题,我一直在为此苦苦挣扎。 我已经看到了一些复杂的解决方案,包括创建假路由器或模拟react-router模块。 但是实际上有一种非常简单的方法可以测试这一点。

If we try to snapshot our component after our API was called, we’ll notice that it renders an empty div.

如果在调用API之后尝试快照组件,则会注意到它呈现了一个空div。

expect(container).toMatchInlineSnapshot(`<div />`);

That’s because the redirection indeed happened, but there was no route to redirect to. From the testing-library renderer perspective, they are no routes defined, we just ask it to render and empty router containing the form.

那是因为确实发生了重定向,但是没有重定向到的路由。 从测试库渲染器的角度来看,它们没有定义路由,我们只是要求它渲染并清空包含表单的路由器。

To ensure that our user gets redirected to /book/1 (as the book's id returned by our API mock is 1), we can add a route for that specific url with a simple text as children.

为了确保我们的用户被重定向到/book/1 (因为我们的API模拟返回的书的ID为1 ),我们可以为该特定url添加一个路由,并以简单文本作为子级。

const { 
    container, 
    getByPlaceholderText, 
    getByText, 
    findByDisplayValue 
  } = render(
    <BrowserRouter>
      <BookCreateForm />
      <Route path="/book/1">Book page</Route>
    </BrowserRouter>
  );

And test that the component rendered the text:

并测试该组件是否呈现了文本:

expect(container).toHaveTextContent(/Book page/);

Our final test :

我们的最终测试:

// BookCreateForm.test.js
import React from 'react';
import { render, act, fireEvent } from '@testing-library/react';
import { BrowserRouter, Route } from 'react-router-dom';


import BookCreateForm from './BookCreateForm';
import api from './api';


jest.mock('./api');


test('it calls api on form submit', async () => {
  api.createBook = jest.fn(() => Promise.resolve({ id: 1 }));
  const { 
    container, 
    getByPlaceholderText, 
    getByText, 
    findByDisplayValue 
  } = render(
    <BrowserRouter>
      <BookCreateForm />
      <Route path="/book/1">Book page</Route>
    </BrowserRouter>
  );


  await act(async () => {
    const input = getByPlaceholderText(/Book's title/);
    fireEvent.change(input, { target: { value: 'Yama Loka Terminus' }});
    await findByDisplayValue(/Yama Loka Terminus/);


    const button = getByText(/Create book/);
    fireEvent.click(button);
  });


  expect(api.createBook).toHaveBeenCalledWith({ title: 'Yama Loka Terminus' });
  expect(container).toHaveTextContent(/Book page/);
});

翻译自: https://medium.com/swlh/how-to-test-react-router-redirection-with-testing-library-c67504ddeec8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值