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);
});
});
});
我正在尝试对将进行几次 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);
});
});
});