茉莉花 - 触发事件不起作用

时间:2018-01-31 19:24:07

标签: javascript jquery ruby-on-rails jasmine jasmine-jquery

我试图测试的代码:

$(".toggle_item").on("change", function() {
  console.log("change triggered")
  item_name = $(this).data("name");
  value = $(this).prop("checked");
  if (Item.isValid(item_name) && cartModule.isUnique(item_name)) { 
    cartModule.toggleItem(item_name, value); 
  }
})

Jasmine spec:

describe("changing toggle item", function() {
  beforeEach(function() {
    console.log("in spec")
    affix(".toggle_item[data-name='ladder']")
    spyOn(cartModule, "isUnique") 
    spyOn(Item, "isValid")
    $(".toggle_item").trigger("change")
  })

  it("should check item for validity & cart for uniqueness", function() {
    expect(Item.isValid).toHaveBeenCalledWith("ladder")
    expect(cartModule.isUnique).toHaveBeenCalledWith("ladder")
  })
})

控制台日志输出表明触发器未触发。日志输出:

> "in spec"

FWIW:

  • 我尝试过同时使用affixloadFixtures
  • 我尝试过同时使用$(".toggle_item").on("change"...$(".toggle_item").change...

宝石清单:

jasmine (2.7.0)
jasmine-core (2.8.0, 2.7.0)
jasmine-jquery-rails (2.0.3)
jasmine-rails (0.14.1)

1 个答案:

答案 0 :(得分:0)

**更新回答:2018-02-03 **

这里的根本问题是,在您的灯具设置之前,您的代码正在运行太早。这与在所有测试之前加载代码不同。在加载之后,您需要能够控制代码运行的时间。

这更多地与您编写代码的方式有关,以便可以对其进行测试,并且在很大程度上独立于实际用于测试的工具和库。

以下是一个例子:

// There is no way to test this expression - it runs as soon as it is loaded
1 + 2


// This is similar to the line above - it tries to add the event listener as soon
// as the line of code is encountered. If you haven't set up fixtures before this line is run
// then you can't test it.
$('.toggle_item').on('change', ...);


// Here is an example of code that is testable
// It is testable because we have separated out the loading of the code 
// from the **invocation** of the code.
function add(){
    // i.e. This expression will only run when `add()` is invoked.
    return 1 + 2;
}


// Here is an example that adds a bit more control over when the event listeners are added.
$(function(){
    // This expression runs when `$.ready` is fired (aka `DOMContentLoaded`)
    // If you can delay `$.ready` until your fixtures are set up then this will work.
    $('.toggle_item').on('change', ...);
})

your linked answer中的解释并不完全正确。 $('.selector').click(...)只是$('.selector').on('click', ...)的别名或简称,因此更改为不会产生任何影响 - 它们在功能上完全相同。

在这种情况下答案实际起作用的原因是因为事件监听器是在$.ready()事件中添加的,而不是在代码加载后立即添加。

执行此操作之间的区别:

console.log('1 before');
console.log('2 middle');
console.log('3 after');

结果:

> 1 before
> 2 middle
> 3 after

运行这个:

console.log('1 before');
$(function(){
    console.log('2 middle');
});
console.log('3 after');

结果:

> 1 before
> 3 after
> 2 middle

另一个可以帮助您的工具是delegated event listeners

基本上,您可以向DOM树中的元素添加单个事件侦听器,该元素侦听来自特定子选择器的事件。

e.g:

$(function(){
    $(document).on('change', '.toggle_item', function(){

    });
})

这样做的好处是可以提前添加事件监听器,.toggle_item之前的事件,但是一旦添加了元素,就会触发监听器。

使用事件代理有一些注意事项(例如,在使用event.preventDefault()event.stopPropagation()时),但它们仍然是一个非常有用的工具。

原始回答

在您测试的代码中,您是否能够在添加事件侦听器时控制/延迟?

我怀疑您的代码在测试中运行.toggle_item语句之前尝试将事件侦听器添加到affix(".toggle_item[data-name='ladder']")

console.log('Adding listener')
$(".toggle_item").on("change", function() {
    console.log("change triggered")
    ...
});

如果你得到的话,你会发现它已经失灵了:

> "Adding listener"
> "in spec"

而不是我们想要的:

> "in spec"
> "Adding listener"