我正在尝试测试axios调用未获得200的HTTP响应的情况。当axios未获得成功的响应时,将引发错误。在这种情况下,我想验证console.log是否被调用了两次。
这是我正在测试的课程的摘要:
class App extends React.Component {
...
async componentDidMount() {
let url = "/api/info/tmp"
try {
let response = await axios.get(url);
...do stuff
this.setState(...);
} catch (e) {
console.log("Could not get " + url);
console.log(e);
}
}
...
}
这是我开玩笑的片段
let mockAxios = new MockAdapter(axios);
...
describe("App - componentDidMount() behavior test", () => {
beforeEach(() => {
app = shallow(<App />);
})
afterEach(() => {
app = undefined;
mockAxios.reset();
});
...
describe("Get " + url + " HTTP response status is not 200", () => {
beforeAll(() => {
mockAxios.onGet(url).reply(302, mockData);
});
it("Does not set state regardless of response body", () => {
console.log = jest.fn();
const state = app.state();
expect(console.log).toHaveBeenCalledTimes(2);
expect(state.solutions).toEqual({});
expect(state.username).toEqual("");
});
});
});
我知道console.log = jest.fn()
位正在做什么,因为在设置它时控制台不再记录伪造的错误。但是,测试失败,因为Expected mock function to have been called two times, but it was called zero times.
我尝试将console.log = jest.fn()
移到“ beforeEach”,“ beforeAll”中并作为全局变量。
更新
我很确定这与所有正在进行的异步有关。 如果我这样做:
it("Does not set state regardless of response body", async () => {
console.log = jest.fn();
await app.instance().componentDidMount();
expect(console.log).toHaveBeenCalledTimes(2);
const state = app.state();
expect(state.solutions).toEqual({});
expect(state.username).toEqual("");
});
然后测试仍然失败,但是我的原因改变了:Expected mock function to have been called two times, but it was called four times.
现在,我只想弄清楚为什么调用它四次而不是两次。
更新2
我弄清楚了为什么console.log被调用了4次!现在,我只需要弄清楚如何重构测试。 如果我注释掉我的笑话模拟,甚至是整个单元测试
it("Does not set state regardless of response body", async () => {
//const state = app.state();
//expect(state.solutions).toEqual({});
//expect(state.username).toEqual("");
//expect(console.log).toHaveBeenCalledTimes(2);
});
然后我可以在控制台中算出确实已经有两个不同的console.log调用。 shallow(<App />)
必须已经在呼叫componentDidMount()
或其他东西。当我添加app.instance().componentDidMount()
时,可以直观地看到它正在记录4次。
答案 0 :(得分:2)
由于您似乎已经知道您在使用模拟程序,所以问题可能与componentDidMount()
有关。
我相信您对shallow(<App />)
的呼叫将已经呼叫App的componentDidMount()
一次(这意味着您的console.log
将被呼叫两次)。
然后,您随后致电app.instance().componentDidMount()
-即再次致电componentDidMount()
()(这意味着您的console.log
将再次被致电两次)。< / p>
因此,总共...有4个呼叫console.log
。
希望能为您指明正确的方向...
实际上,您的问题看起来与[关于如何"How to mock console when it is used by a third-party library?"的StackOverFlow问题
您可以使用Jest mock functions来spyOn
对象global.console
。
例如,您的测试可能如下所示:
// Setup jest to spy on the console
const consoleSpy = jest.spyOn(global.console, 'log')
describe('App - componentDidMount() behavior test', () => {
beforeEach(() => {
jest.resetAllMocks() // reset your consoleSpy state back to initial
app = shallow(<App />)
})
...
it('Does not set state regardless of response body', () => {
const spy = jest.spyOn(global.console, 'log')
const state = app.state()
expect(consoleSpy).toHaveBeenCalledTimes(2)
expect(state.solutions).toEqual({})
expect(state.username).toEqual('')
})
...
答案 1 :(得分:1)
理想情况下,将API调用移至componentDidMount
之外,并移入其自己的类method
中。可以通过生命周期方法或事件回调手动调用它。另外,您应该预期响应会以某种方式影响您的UI state
(例如:向用户显示请求失败的消息,然后重试)。
以下示例可以用.then/.catch
代替async/await
完成。无论哪种方式,您都在使用Promises
的{{1}},因此他们需要asynchronous
测试。
注意:以下假设asynchronous
在disableLifecycleMethods
适配器中为true。另外,仅测试enzyme
的更改(或state
)有点多余;相反,您将测试是否根据当前的console.log
渲染了一个组件。
工作示例:https://codesandbox.io/s/939w229l9r(包括state
和end to end
测试---您可以通过点击integration
来运行测试标签位于沙箱左下角附近)
App.js (这将是一个container,其中包含所有相关的Tests
,并根据需要将其分散到其state
中)
children
App.test.js (假设您要进行import React, { Component } from 'react';
class App extends Component {
state = = {
error: "",
isLoading: true,
solutions: {},
username: ""
};
componentDidMount() {
this.fetchData("/api/info/tmp");
}
fetchData = async (url) => {
try {
const res = await axios.get(url);
...do stuff
this.setState({
error: "",
isLoading: false,
solutions: res.data.solutions,
username: res.data.username
});
} catch (err) {
this.setState({
error: err,
isLoading: false,
solutions: {},
username: ""
});
}
}
render() { ... }
}
测试)
end to end
App.test.js (假设您要进行import { shallow } from 'enzyme';
import App from './App';
const timeout = () =>
new Promise(resolve => {
setTimeout(() => {
resolve();
}, 2000);
});
const initialState = {
error: "",
isLoading: true,
solutions: {},
username: ""
};
describe("App", () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<App />);
wrapper.setState({ ...initialState });
});
afterAll(() => {
wrapper.unmount();
});
it("sets data to state based upon successful API call", async () => {
wrapper.instance().fetchData("/api/info/tmp");
await timeout();
wrapper.update();
expect(wrapper.state('isLoading')).toBeFalsy();
expect(wrapper.state('solutions')).toEqual({ somedata });
expect(wrapper.state('username')).toEqual("Some User");
});
it("displays an error upon unsuccessful API call", async () => {
wrapper.instance().fetchData("/api/bad/url");
await timeout();
wrapper.update();
expect(wrapper.state('isLoading')).toBeFalsy();
expect(wrapper.state('solutions')).toEqual({});
expect(wrapper.state('username')).toEqual("");
expect(wrapper.state('error')).toEqual("No data found.");
});
});
测试)
integration
答案 2 :(得分:0)
我知道了!有点...我不确定为什么为什么这样工作,但是在实际的“ it”中设置模拟却不起作用。
解决方案是制作一个beforeEach
和afterEach
describe("Get " + url + " HTTP response status is not 200", () => {
beforeAll(() => {
mockAxios.onGet(url).reply(302, mockData);
});
beforeEach(() => {
console.log = jest.fn();
});
afterEach(() => {
jest.resetAllMocks();
});
it("Does not set state regardless of response body", async () => {
const state = app.state();
expect(state.solutions).toEqual({});
expect(state.username).toEqual("");
expect(console.log).toHaveBeenCalledTimes(2);
});
});