Calling trigger() doesn't always fire when cal

2019-07-21 05:48发布

问题:

Although I've used QUnit in the past, it's been a year since I really used it, so I am hoping this is something trivial!

I have a bunch of QUnit tests that are working really well, apart from one that fails every other time that the page is loaded. If I refresh, the test works, but if I refresh again it breaks! It's become predictable but I cannot see why it's not working. The test in question should use a trigger('change') event, but this only fires every other time.

I have a dynamically populated select that I bind a change event to. All that it does it save the value to localStorage and the test I have just checks the value has been set.

Example HTML:

<select id="options">
    <option value="">Please choose</option>
</select>
<p id="result"></p>

Example JS:

$(document).ready(function() {
    var data = {
        1: 'Option 1',
        2: 'Option 2',
        3: 'Option 3'
    }
    for(var d in data) {
        $('#options').append($('<option>').val(d).text(data[d]));
    }
    $('#options').on('change', function() {
        $('#result').text(this.value);
        if(this.value != '') {
            localStorage.setItem('optionValue') = this.value;
        } else {
            throw 'You must choose a team';
        }
    });
});

Example QUnit tests:

test('Test some other stuff', function(assert) {
    localStorage.removeItem('optionValue');
    // some tests to check things have worked correctly
});

test('Is value added to localStorage?', 2, function(assert) {
    throws(function() { throw new $('#chooseTeam').val('').change(); }, 'No option selected');
    $('#options').val(1).trigger('change');
    equal(localStorage.getItem('optionValue'), 1, 'The first item should be selected');
});

However, the result in the QUnit output is undefined. I narrowed it down to the first test and then was able to see that it's the line localStorage.removeItem('optionValue') that is called every time but the change event is only triggered every other time.

I've create a JS fiddle to try and demonstrate the problem: http://jsfiddle.net/cchana/zqNtU/3/ (the demo shows how it should work, but not the failing tests)

Is there something obvious that would make this fail anyway?

UPDATE

Thought I should update with some information that may help...

In the change method, I added a console.log(), like so:

$('#options').on('change', function() {
    console.log('here');
});

I just wanted to check that the change event was actually being triggered, but just as the tests show, the method is only triggered every other time.

UPDATE 2

To get my tests passing, I have removed the line localStorage.removeItem('optionValue'); not ideal and I would still like to get to the bottom of it if at all possible!

回答1:

I found the answer from this article (solving the mystery behind alternating failed tests in qunit)

To explain why, you probably have a callback/plugin automatically binds to existing elements on the page. So when the file containing the plugin is included in the page, the elements are binded automatically.

So when you run the tests the first time, it fails. Why?! The reason is that when the tests are run, the elements in the test harness are seemingly re-created on each run of the tests. So when they are re-created, the automatic binding does not take place since the JavaScript file has already been included.

If so, then why does it work on alternate invocations? Ah… therein lies the magic. To make tests more efficient, QUnit will run the failed tests before other tests upon page refresh. So, when the failed tests are executed before other tests, the elements in the test harness that are already present are binded, and thus, the tests PASS! But because these tests now pass, when the page is refreshed they go back to the same order as they originally was, and thus they now FAIL!

Of course with this realization, it prompted me to rewrite the plugin more efficiently. Hope this self-discovery is helpful to someone out there! Have fun testing!

I hope this helps you.