【发布时间】:2018-10-16 10:51:15
【问题描述】:
我目前正在为我们的一个项目创建一个新的 React 组件,我几乎无法为它编写一个合适的测试。我已经阅读了很多文档和博客文章等等,但我似乎无法让它运行。
TL;DR
对我来说,Promise 似乎没有被执行。当我使用调试器运行测试时,它不会在 Promise 的函数中停止,也不会在 then() 函数中停止。但是,它会在测试本身的 then/catch 函数中停止。
代码
因此,该组件实际上相当简单。目前它应该通过 API 搜索位置。它的测试如下所示:
import axios from 'axios';
import React from 'react';
import {shallowWithIntl} from "../../../Helpers/react-intl-helper";
import Foo from "../../../../src/Components/Foo/Foo";
import {mount} from "enzyme";
const queryTerm = 'exampleQueryTerm';
const locationAgs = 'exampleLocationKey';
const fakeLocationObject = {
search: '?for=' + queryTerm + '&in=' + locationAgs
};
jest.mock('axios', () => {
const exampleLocations = [{
data: {"id": "expected-location-id"}
}];
return {
get: jest.fn().mockReturnValue(() => {
return Promise.resolve(exampleLocations)
})
};
});
let fooWrapper, instance;
beforeEach(() => {
global.settings = {
"some-setting-key": "some-setting-value"
};
global.URLSearchParams = jest.fn().mockImplementation(() => {
return {
get: function(param) {
if (param === 'for') return queryTerm;
else if (param === 'in') return locationAgs;
return '';
}
}
});
fooWrapper = shallowWithIntl(<Foo location={fakeLocationObject} settings={ global.settings } />).dive();
instance = fooWrapper.instance();
});
it('loads location and starts result search', function() {
expect.assertions(1);
return instance
.searchLocation()
.then((data) => {
expect(axios.get).toHaveBeenCalled();
expect(fooWrapper.state('location')).not.toBeNull();
})
.catch((error) => {
expect(fooWrapper.state('location')).toBe(error);
});
});
因此,如您所见,测试应该在 Foo 组件实例上调用 searchLocation,该实例返回一个 Promise 对象,正如您(几乎)可以在其实现中看到的那样。
import React, { Component } from 'react';
import { injectIntl } from "react-intl";
import {searchLocationByKey} from "../../Services/Vsm";
class Foo extends Component {
constructor(props) {
super(props);
this.state = {
location: null,
searchingLocation: false,
searchParams: new URLSearchParams(this.props.location.search)
};
}
componentDidUpdate(prevProps) {
if (!prevProps.settings && this.props.settings) {
this.searchLocation();
}
}
searchLocation() {
this.setState({
searchingLocation: true
});
const key = this.state.searchParams.get('in');
return searchLocationByKey(key)
.then(locations => {
this.setState({ location: locations[0], searchingLocation: false })
})
.catch(error => console.error(error));
}
render() {
// Renders something
};
}
export default injectIntl(Foo);
输入searchLocationByKey:
function requestLocation(url, resolve, reject) {
axios.get(url).then(response => {
let locations = response.data.map(
location => ({
id: location.collectionKey || location.value,
rs: location.rs,
label: location.label,
searchable: location.isSearchable,
rawData: location
})
);
resolve(locations);
}).catch(error => reject(error));
}
export const searchLocationByKey = function(key) {
return new Promise((resolve, reject) => {
let url = someGlobalBaseUrl + '?regional_key=' + encodeURIComponent(key);
requestLocation(url, resolve, reject);
});
};
问题
这是测试的输出:
Error: expect(received).toBe(expected)
Expected value to be (using ===):
[Error: expect(received).not.toBeNull()
Expected value not to be null, instead received
null]
Received:
null
我不得不承认我对 Promises、React 和 JavaScript 测试还很陌生,所以我可能混淆了几件事。正如我上面所写的,Promise 似乎没有正确执行。调试时不会停在Foo.searchLocation中定义的then()函数中。相反,显然,测试中定义的 then() 和 catch() 函数都被执行了。
我已经在这个问题上花费了太多时间,我不知道如何继续。我做错了什么?
更新 1:done() 函数
正如 El Aoutar Hamza 在下面的答案中指出的那样,可以将函数(通常称为“完成”)传递给测试函数。我就是这样做的:
it('loads location and starts result search', function(done) {
expect.assertions(1);
return instance
.searchLocation()
.then((data) => {
expect(fooWrapper.state('location')).not.toBeNull();
done();
})
.catch((error) => {
expect(fooWrapper.state('location')).toBe(error);
});
});
但我最终得到了这个错误:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
【问题讨论】:
标签: javascript reactjs testing jestjs es6-promise