3

I am testing a Backbone View with Jasmine 2.0. The View makes an initial ajax request on load to populate its form field with values. I want my Jasmine test (in the it function below) to run only after those values are received.

The test currently fails because it seems the ajax request is received after the test runs, even though I am stubbing the ajax request.

How should I be writing this test?

describe('View', function() {

  var formFieldValues = [/* blah */];
  var view;

  beforeEach(function(done) {
    jasmine.Ajax.install();
    jasmine.Ajax.stubRequest('/form-fields').andReturn({
      responseJSON: formFieldValues
    });
    view = new View({el: $('<main></main>')}); // makes ajax call on initialization
    $('body').append(view.el);
  });

  afterEach(function() {
    view.remove();
    jasmine.Ajax.uninstall();
  });

  it('form fields have values', function(done) {
    // PROBLEM IS HERE: THE FOLLOWING RUNS BEFORE VIEW'S AJAX CALL FINISHES
    expect(view.$('[name="fieldName"]').children().length).toEqual(formFieldValues.length);
  });

});
4

2 回答 2

6

看起来您实际上并没有运行异步规范。我注意到你beforeEachit他们都收到了done回电,但他们都没有打电话。如果您正在使用 ajax 存根,那么您可能不需要运行异步规范,因为存根将有效地使 ajax 请求同步。

当我运行以下代码时,一切正常。

规格:

describe('View', function() {

  var formFieldValues = [{name: 'foo'}, {name: 'bar'}];
  var view;

  beforeEach(function() {
    jasmine.Ajax.install();
    jasmine.Ajax.stubRequest('/form-fields').andReturn({
      responseText: JSON.stringify(formFieldValues)
    });
    view = new View(); // makes ajax call on initialization
  });

  afterEach(function() {
    jasmine.Ajax.uninstall();
  });

  it('form fields have values', function() {
    // PROBLEM IS HERE: THE FOLLOWING RUNS BEFORE VIEW'S AJAX CALL FINISHES
    expect(view.$('[name="fieldName"]').children().length).toEqual(formFieldValues.length);
  });

});

我的虚拟实现:

Collection = Backbone.Collection.extend({
  url: '/form-fields'
});

View = Backbone.View.extend({
  initialize: function() {
    this.collection = new Collection();
    this.collection.on('reset', this.render, this);
    this.collection.fetch({reset: true});
  },

  render: function() {
    var innerEl = $('<div name="fieldName"></div>')
    this.collection.each(function(item) {
      innerEl.append('<span></span>');
    });
    this.$el.append(innerEl);
  }
});
于 2014-03-08T04:00:54.623 回答
0

应该使用runs() 和 waitFor() 方法使其异步。对 run() 方法的调用充当单独的测试块,其中最后一个将进行异步调用。在最简单的情况下,可以使用 setTimeout() 来模拟异步行为。在更复杂的用法中,将进行实际调用,例如对启用 Ajax 的对象进行调用。

waitsFor() 方法接受三个参数:“latch”函数、失败消息、超时(以毫秒为单位)

runs(function() {
    flag = false;
    value = 0;

setTimeout(function() {
  value++;
  //keep returning false…
  flag = false;
}, 500);
  });

  waitsFor(function() {
    return flag;
  }, "The Value should be incremented", 5000);
});

或者你可以尝试制作真正的 Ajax 请求

当需要执行集成测试时,您可能希望执行真正的 Ajax 调用,而不仅仅是模仿它们。没问题。只需创建一个间谍,进行 Ajax 调用,然后是 waitsFor(),您可以在其中检查 callback.callCount 属性或 what-have-you: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

it("should make a real AJAX request", function () {
    var callback = jasmine.createSpy();
    makeAjaxCall(callback);
    waitsFor(function() {
        return callback.callCount > 0;
    }, "The Ajax call timed out.", 5000);

    runs(function() {
        expect(callback).toHaveBeenCalled();
    });
});

function makeAjaxCall(callback) {
    $.ajax({
        type: "GET",
        url: "data.json",
        contentType: "application/json; charset=utf-8"
        dataType: "json",
        success: callback
    });
}
于 2014-03-12T05:59:20.490 回答