Jasmine 测试 - 附加元素的持久化以及为什么点击触发不止一次

Jasmine testing - persistence of affixed elements and why clicks trigger more than once

请注意这是特定于使用 Jasmine 的测试环境。我在开发模式下没有这些问题。这是我创建的一个简单示例:

代码

function mountTest() {
  $(document).on("click", ".test", function() {
    console.log(">> clicked test")
  })
}

规格代码

fdescribe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
        mountTest() /******************** KEY LINE ****************/
    })
    describe("1st spec", function() {
        beforeEach(function() {
            console.log(">>1")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("2nd spec", function() {
        beforeEach(function() {
            console.log(">>2")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("3rd spec", function() {
        beforeEach(function() {
            console.log(">>3")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
        describe("4th spec", function() {
            beforeEach(function() {
                console.log(">>4")
                test_btn.click()
            })
            it("shoudl work", function(){

            })
        })
    })
})

就目前而言,控制台中的输出是这样的:

>>1
>> clicked test
>>2
>> clicked test
>> clicked test
>>3
>> clicked test
>> clicked test
>> clicked test
>>3
>> clicked test
>> clicked test
>> clicked test
>> clicked test
>>4
>> clicked test
>> clicked test
>> clicked test
>> clicked test

我发现点击冗余的原因是因为 mountTest() 在顶级 beforeEach 被调用。进一步的实验表明,如果 mountTest() 在作为规范(1、2、3、4)的 describe 块之一中被调用,那么在第一个规范之后,所有规范的冗余都会被删除其中 mountTest() 被放置。例如

修改规格代码

fdescribe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
    })
    describe("1st spec", function() {
        beforeEach(function() {
            console.log(">>1")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("2nd spec", function() {
        beforeEach(function() {
            console.log(">>2")
            mountTest() /******************** KEY LINE ****************/
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("3rd spec", function() {
        beforeEach(function() {
            console.log(">>3")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
        describe("4th spec", function() {
            beforeEach(function() {
                console.log(">>4")
                test_btn.click()
            })
            it("shoudl work", function(){

            })
        })
    })
})

控制台输出

>>1
>>2
>> clicked test
>>3
>> clicked test
>>3
>> clicked test
>>4
>> clicked test

冗余消失了。第一个规范不会触发任何点击,因为 mountTest() 在第二个规范之前不会被调用。

有人可以向我解释一下这种行为吗?我什至不知道我在这里误解了什么...是 affixbeforeEach 还是...的性质?

以下是我在查看此内容时的一些问题:

  1. 在修改后的规格示例中,如果 mountTest() 仅在第二个规格 describe 块中调用,为什么第三个/第四个规格的点击有效? mountTest() 是怎么翻译过来的?我唯一的想法是,affix(".test") 元素以某种方式保留了 mountTest() 添加的 eventHandler,但根据 jasmine-fixture 文档 (https://github.com/searls/jasmine-fixture),词缀清理了它的每个规范后都有自己的元素?

  2. 在最初的规范示例中,我认为点击次数如此多余的解释是 mountTest() 在第 N 个规范中安装了 N 次,因此在第 N 个规范中,每次点击为每个挂载注册一次,因此您将获得第 N 次点击。但是与#1 重叠,如果 affix(".test") 元素每次都是新的,那么你每次都安装在一个新对象上,所以它应该只触发一次,不是吗?

FWIW,为了测试词缀,我确实手动编写了一个 afterEach(function() { $(".test").remove() }) 并将其添加到原始规范代码中的每个 it 期望块之后,这根本没有解决问题。

相关的 gemfile:

gem 'jasmine', "2.6"
gem 'jasmine-jquery-rails' # resolving to 2.0.3

然后 jasmine-fixture 也通过文件 spec > javascripts > helpers > jasmine-fixture.min.js 加载,这是因为在 spec > javascripts > support > jasmine.yml 文件中有一行:

src_files:
...
helpers:
  - 'helpers/**/*.js'

问题是侦听器附加到文档而不是 affix 管理的元素。这就是为什么没有删除侦听器以及为什么手动删除元素也没有任何作用的原因。您需要将其直接附加到创建的元素。你可以这样做:

function mountTest(el) {
  el.on("click", function() {
    console.log(">> clicked test")
  })
}

并这样称呼它:

beforeEach(function() {
    test_btn = affix(".test")
    mountTest(test_btn);
})

这会将其直接附加到元素上,而不是 document

@MinusFour 回答正确,我接受了,出于流程原因,我只是想根据解释 + 评论澄清两个可行的答案。

选项A:

describe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
    })
    beforeAll(function(){
        mountTest()
    })
    ....

选项 B:

describe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
        mountTest()
    })
    afterEach(function() {
        $(document).off()
    })
    ....