Тестирование функций обратного вызова JavaScript с помощью жасмина
У меня есть следующие функции:
function getPersonData(id) {
retrieveData(
id,
function(person) {
if(person.name) {
displayPerson(person);
}
}
}
function retrieveData(id, successCallBack) {
executeRequest(id, {
success: successCallBack
});
}
getPersonData
извлекает информацию о человеке на основе идентификатора Это в свою очередь вызывает retrieveData
путем передачи идентификатора и функции successCallBack.
retrieveData
принимает id и successCallBack и вызывает другую функцию, executeRequest
, который получает данные и передает обратно объект человека.
Я пытаюсь проверить getPersonData
и настроить следующую спецификацию
describe("when getPersonData is called with the right person id", function() {
beforeEach(function() {
spyOn(projA.data, "retrieveData").and.returnValue(
{
'name': 'john'
}
);
spyOn(projA.data, "displayPerson");
projA.data.getPersonData("123");
});
it("displays the person's details", function() {
expect(projA.data.displayPerson).toHaveBeenCalled();
);
});
Но когда спецификация выполнена displayPerson
метод не вызывается. Это связано с тем, что данные о человеке возвращаются из успешного обратного вызова. function(person)
не передается, хотя я издевался retrieveData
вернуть результат.
Мой вопрос: это правильный способ проверить функции callBack? В любом случае, что я делаю не так?
1 ответ
Итак, жасмин сложна во многих тонких аспектах, и я думаю, что есть два основных вопроса с вашим кодом
- У вас слишком много асинхронных вызовов, обернутых друг в друга. Что само по себе не проблема, но делает тестирование в JASMINE чертовски сложным. Например, какой смысл иметь
retrieveData
функция, которая просто вызываетexecuteRequest
функция с точно такими же параметрами, но немного по-другому.
Я переписал ваш getPersonData, чтобы быть таким
function getPersonData(id) {
// this is needed to properly spy in Jasmine
var self = this;
//replaced retrieveData with just execute request
// self is required to properly spy in Jasmine
self.executeRequest(id, {
success: function(person) {
if (person.name) {
self.displayPerson(person);
}
}
})
}
//I don't know what exactly executeRequest does
//but I took the liberty to just make it up for this example
function executeRequest(id, callback) {
callback.success({
name: id
});
}
//I also assumed that projA would look something like this
var projA = {
data: {
getPersonData: getPersonData,
retrieveData: retrieveData,
displayPerson: displayPerson,
executeRequest: executeRequest
}
};
2. Чтобы протестировать асинхронный код в Jasmine, необходимо включить done
Обратный звонок с тестом. Кроме того, если вы ожидаете, что функция обратного вызова будет запускаться автоматически, вам нужно настроить ее в setTimeout
функция, иначе она никогда не сработает. Вот скорректированный пример:
describe("when getPersonData is called with the right person id", function() {
beforeEach(function() {
//you should not spyOn retriveData or executeCode because it will override the function you wrote and will never call displayPerson
// you should only have spies on the methods you are testing otherwise they will override other methods
spyOn(projA.data, "displayPerson");
});
it("displays the person's details", function(done) {
// it's better to call the function within specific test blocks so you have control over the test
projA.data.getPersonData("123");
// at this point, it will just have called the getPersonData but will not call executeRequest
setTimeout(function() {
//this block will just call executeRequest
setTimeout(function() {
//this block will finally call displayPerson
expect(projA.data.displayPerson).toHaveBeenCalled();
//tell jasmine the test is done after this
done();
})
})
});
})