axios 返回的测试数据更新 DOM

Testing data returned by axios updates the DOM

我正在尝试对将进行几次 API 调用的 React 组件进行 TDD,但我正在努力研究如何测试响应是否会按预期影响 DOM。在此示例中,我请求获取帖子列表,更新状态,然后更新 DOM 以显示所述列表。

这在现实中有效,但我的测试失败了,因为它找不到我正在寻找的 li 元素(它找到 0,而不是 1),尽管我能够在错误输出中看到该元素。

我哪里错了?随意将我的测试设置撕成碎片,我刚刚开始掌握 TDDing React 组件。

// PostList.js

import React from "react";
import axios from "axios";

class PostList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      posts: []
    };

    this.getPosts = this.getPosts.bind(this);
  }

  async getPosts() {
    return axios.get("/api/posts/").then(response => {
      return response.data.posts;
    });
  }

  componentDidMount() {
    this.getPosts().then(posts => {
      this.setState({
        posts: posts
      });
    });
  }

  render() {
    const posts = this.state.posts.map(post => (
      <li key={post.id}>
        <strong>{post.title}</strong> {post.description}
      </li>
    ));

    return (
      <div>
        <h1>Posts:</h1>
        <ul>{posts}</ul>
      </div>
    );
  }
}

export default PostList;
// PostList.test.js

import React from "react";
import { shallow } from "enzyme";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";

import PostList from "./PostList";

describe("<PostList />", () => {
  let shallowPostList;
  let posts = [{ id: 1, title: "Hello", description: "World" }];

  const getPostsMock = new MockAdapter(axios);

  const PostList = () => {
    if (!shallowPostList) {
      shallowPostList = shallow(<PostList />);
    }

    return shallowPostList;
  };

  getPostsMock
    .onGet("/api/posts/")
    .reply(200, { posts });

  beforeEach(() => {
    shallowPostList = undefined;
  });

  describe("render()", () => {
    it("renders one post item when one post exists", done => {
      const PostListItems = PostList().find("li");

      setTimeout(() => {
        expect(PostListItems).toHaveLength(1);
        done();
      }, 1);
    });
  });
});

我认为是否是 TDD 并不是一个必要的细节。我对你的方法也有点困惑。

解决方案也取决于您的测试框架。 React 最流行的是 Jest,因此我可以想出一个非常适合它的解决方案。

如果我是你,我会将异步函数分离到一个单独的文件中,从而简化模拟。

import axios from 'axios';

const getPosts = async () => axios.get('/api/posts/');

export default getPosts;

假设您在通常的 src 文件夹中有一个 PostList 组件以及 index.js 文件.

.
├── src
│   ├── index.js
│   ├── PostList
│       ├── __mocks__
│          ├── GetPosts.js
│       ├── PostList.js
│       ├── PostList.test.js
│       ├── GetPosts.js

__mocks__ 文件夹被 Jest 识别并按预期工作,只要遵循文件命名约定:

  • 文件名应与被模拟的文件名相同。

另外注意在测试文件中使用jest.mock('...')

根据您的模拟示例,您可以在 __mocks__/GetPosts.js.

中定义类似的内容
const returnedData = [{
   id: 1,
   title: "Hello",
   description: "World"
}];

const getPosts = jest.fn().mockReturnValueOnce(() => returnedData);

export default getPosts;
// PostList.js 
...
async componentDidMount() {
   const posts = await GetPosts();
   this.setState({
      posts,
   });
}
...

你的测试文件应该是这样的:

import React from 'react';
import { shallow } from 'enzyme';
import PostList from './PostList.js';

jest.mock('./GetPosts.js'); // Here you "tell" to Jest to mock the function.

describe('<PostList />', () => {
   let wrapper;

   beforeAll(async () => {
      wrapper = await shallow(<PostList />);
   });

   describe('componentDidMount', () => {
      it('renders an li tag', () => {
         expect(wrapper.find('li')).toHaveLength(1);
      });
   });
});