Почему я должен взывать к обещанию?затем () сам проверить ES6 обещает?
Это следующий вопрос к этому (не обязательно читать этот вопрос, чтобы ответить на этот).
Возьмем в качестве примера следующий компонент React:
class Greeting extends React.Component {
constructor() {
fetch("https://api.domain.com/getName")
.then((response) => {
return response.text();
})
.then((name) => {
this.setState({
name: name
});
})
.catch(() => {
this.setState({
name: "<unknown>"
});
});
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
Используя Jest, вот как мы можем утверждать, что name
равно некоторому тексту, возвращенному из запроса getName
:
test("greeting name is 'John Doe'", () => {
const fetchPromise = Promise.resolve({
text: () => Promise.resolve("John Doe")
});
global.fetch = () => fetchPromise;
const app = shallow(<Application />);
return fetchPromise.then((response) => response.text()).then(() => {
expect(app.state("name")).toEqual("John Doe");
});
});
Но следующее Не кажется правильным:
return fetchPromise.then((response) => response.text()).then(() => {
expect(app.state("name")).toEqual("John Doe");
});
Я имею в виду, что я несколько копирую реализацию в тестовом файле.
Мне кажется неправильным, что я ... придется вызывать then()
или catch()
непосредственно в моих тестах. Особенно когда response.text()
также возвращает обещание, и у меня есть два цепных then()
s, чтобы просто утверждать, что name
равно John Doe
.
Я пришел из углового, где можно было просто позвонить $rootScope.$digest()
и сделать утверждение после этого.
2 ответа:
Я отвечу на свой собственный вопрос после обсуждения этого вопроса с коллегой по работе, который сделал для меня все более ясным. Может быть, вопросы выше уже ответили на мой вопрос, но я даю ответ в словах, которые я могу лучше понять.
Я не столько вызываю
then()
себя из первоначальной реализации, я только цепляю другойthen()
, который будет выполнен после других.Кроме того, лучшей практикой является размещение моего
fetch()
вызова и всех егоthen()
s иcatch()
s в его собственной функции и вернуть обещание, как это:requestNameFromApi() { return fetch("https://api.domain.com/getName") .then((response) => { return response.text(); }) .then((name) => { this.setState({ name: name }); }) .catch(() => { this.setState({ name: "<unknown>" }); }); }
И тестовый файл:
test("greeting name is 'John Doe'", () => { global.fetch = () => Promise.resolve({ text: () => Promise.resolve("John Doe") }); const app = shallow(<Application />); return app.instance().requestNameFromApi.then(() => { expect(app.state("name")).toEqual("John Doe"); }); });
Что имеет больше смысла. У вас есть "функция запроса", возвращающая обещание, вы непосредственно проверяете вывод этой функции, вызывая ее и цепочку другого
then()
, который будет вызван в конце, чтобы мы могли безопасно утверждать то, что нам нужно.Если вас интересует альтернатива возвращению обещания в тесте, мы можем написать тест выше следующим образом:
test("greeting name is 'John Doe'", async () => { global.fetch = () => Promise.resolve({ text: () => Promise.resolve("John Doe") }); await shallow(<Application />).instance().requestNameFromApi(); expect(app.state("name")).toEqual("John Doe"); });
Мой ответ, вероятно, отстой,но я впервые протестировал эту функцию недавно, а также с обещаниями fetch. И этот же аспект смущал меня.
Я думаю, что вы не дублируете код в реализации настолько, насколько вам приходится откладывать свое ожидание до тех пор, пока асинхронная часть вашего макета/двойного обещания теста не завершится. Если вы поместите ожидание вне then, ожидание будет выполняться до того, как будет выполнена часть stub, которая задает ваше состояние.
В основном ваш test double по-прежнему является обещанием и ожидает, что предложения в тесте, которые зависят от того, что double выполнил, должны быть в предложении then.