Тестирование функций обратного вызова 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 ответ

Итак, жасмин сложна во многих тонких аспектах, и я думаю, что есть два основных вопроса с вашим кодом

  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();
        })
       })
     });
    })
Другие вопросы по тегам