В настоящее время я создаю новый компонент React для одного из наших проектов, и я в значительной степени застрял в написании соответствующего теста для него. Я прочитал довольно много документов и постов в блоге и многое другое, но я не могу заставить его работать.
TL; DR
Мне кажется, что Обещание не выполнено. Когда я запускаю тест с отладчиком, он не останавливается ни в функции 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);
});
});
Итак, как вы можете видеть, тест должен вызывать searchLocation
для экземпляра компонента Foo, который возвращает объект 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, поэтому я мог бы перепутать несколько вещей. Как я писал выше, кажется, что Обещание не выполняется должным образом. При отладке он не остановится в функции then (), определенной в Foo.searchLocation
. Вместо этого, очевидно, выполняются обе функции then () и catch (), определенные в тесте.
Я уже потратил слишком много времени на эту проблему, и я не знаю, что делать дальше. Что я делаю не так?
Обновление 1: функция done ()
Как указал Эль Аутар Хамза в ответе ниже, можно передать функцию (обычно называемую «выполнено») тестовой функции. Я сделал именно это:
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.