Тест Qunit чередуется между pass и fail при обновлении страницы

У меня есть два теста, которые вызывают побочные эффекты друг с другом. Я понимаю, почему, поскольку я заменяю встроенную функцию jQuery, которая внутренне вызывается во втором тесте. Однако я не понимаю, почему тест поочередно проходит и терпит неудачу.

Этот вопрос аналогичен Однако я ничего не делаю непосредственно на div qunit-fixture.

Вот мои тесты

test('always passing test', function() { // Always passes
    var panelId = '#PanelMyTab';

    var event = {};
    var ui = {
        tab: {
            name: 'MyTab',
        },
        panel: panelId,
    };

    $('<div id="' + panelId + '">')
        .append('<a href="#" class="export">Test</a>')
        .append('<a href="#" class="showForm">Show Form</a>')
        .appendTo('#qunit-fixture');

    jQuery.fn.on = function(event, callback) {
        ok(this.selector == panelId + ' .export', 'Setting export click event');
        equal(callback, tickets.search.getReport, 'Callback being set');
    };

    loadTab(event, ui);
});

test('alternately passing and failing', function() { // Alternates between passing and failing on page refresh
    expect(5);

    var testUrl = 'test';
    $('<div class="ui-tabs-panel">')
        .append('<a href="'+ testUrl + '" id="getReport">Get Report</a>')
        .append('<form action="notest" target="" class="ticketSearch"></form>')
        .appendTo('#qunit-fixture');

    // Setup form mocking
    $('form.ticketSearch').submit(function() {
        var urlPattern = new RegExp(testUrl + '$');
        ok(urlPattern.test($(this).prop('action')), 'Form action set to link href');
        equal($(this).prop('target'), '_blank', 'Open form on a new page');
    });

    var event = {
        target: 'a#getReport',
    };

    var result = getReport(event);

    var form = $('form.ticketSearch');

    ok(/notest$/.test($(form).prop('action')), 'Making sure action is not replaced');
    equal($(form).prop('target'), '', 'Making sure that target is not replaced');

    ok(false === result, 'click event returns false to not refresh page');
});

Тесты начнутся, но при обновлении они будут чередоваться между прохождением и провалом.

Почему это происходит? Даже добавление параметров GET к URL-адресу приводит к тому же поведению на странице.

В случае сбоя тест не работает, потому что внутренний jQuery вызывает .on(), когда установлен обработчик submit(). Но почему в этом случае тест всегда не срабатывает? Что браузер делает, что состояние сохраняется во время обновления страницы?

Update:

Вот код, который тестируется:

var tickets = function() {
    var self = {
        loadTab: function(event, ui) {
            $(panel).find('.export').button().on('click', this.getReport);
        },

        search: {
            getReport: function(event) {
                var button = event.target;
                var form = $(button).closest('div.ui-tabs-panel').find('form.ticketSearch').clone(true);

                $(form).prop('action', $(button).prop('href'));
                $(form).prop('target', '_blank');

                $(form).submit();

                return false;
            }
        }
    };

    return self;
}();

Ответ 1

Я изменил @Ben fiddle, чтобы включить ваш код в оба теста. Я изменил некоторые из вашего кода, чтобы он работал правильно. Когда вы нажмете кнопку запуска, все тесты пройдут. Когда вы снова нажмете кнопку "Пробег", второй тест ( "чередование и провал" ) потерпит неудачу - это в основном имитирует вашу исходную проблему.

Проблема - ваш первый тест ( "всегда проходящий тест" ) изменяет глобальное состояние, заменив функцию jQuery.fn.on на переопределенную. Из-за этого, когда тесты выполняются по порядку, второй тест ( "поочередно передающий и неудачный" ) использует неправильную переопределенную функцию jQuery.fn.on и не работает. Каждый unit test должен вернуть глобальное состояние обратно в свое состояние перед тестированием, чтобы другие тесты могли выполняться на основе тех же предположений.

Причина, по которой он чередуется между pass и fail, заключается в том, что под капотом QUnit всегда сначала запускает неудачные тесты (он как-то помнит об этом через cookie или локальное хранилище, я не совсем уверен). Когда он сначала запускает неудачные тесты, второй тест выполняется до первого; в результате второй тест получает функцию jQuery native on и работает. Когда вы запустите его в третий раз, тесты будут выполняться в их "оригинальном" порядке, а во втором тесте будет использоваться переопределенная функция on и сбой.

Здесь работает fiddle. Я добавлю исправление к "un-override" функции on после теста путем кэширования исходной функции var jQueryOn = jQuery.fn.on; и сброса ее в конце теста с помощью: jQuery.fn.on = jQueryOn;. Вы, вероятно, лучше реализуете это, используя метод QUnit module teardown().

Вы можете проверить https://github.com/jquery/qunit/issues/74 для получения дополнительной информации.

Ответ 2

Я не уверен, что смогу решить эту проблему без дополнительной информации, но я могу указать на некоторые возможные проблемы.

Первый тест, похоже, имеет неверный синтаксис в строке 2

var panelId = '#PanelMyTab');

Но это, вероятно, ошибка типа, видя, что вы говорите, что первое всегда проходит.

Я предполагаю, что для того, чтобы первый тест прошел (и был действителен), loadTab (event, ui) должен запустить jQuery.fn.on(), без которого не было никаких утверждений. Что делает некоторое тестирование с вкладками пользовательского интерфейса jQuery, похоже, это так (просто не уверен, что это было ваше намерение).

Я не уверен, что это целесообразно помещать эти утверждения в эту функцию, и вы должны понимать, что вы перезаписали функцию jquery с помощью функции, которая ничего не делает, поэтому она может вызвать проблемы.

Кажется, что вы делаете что-то подобное во втором тесте, вы ожидаете 5 утверждений, но я могу видеть только, как можно запустить последний 3

ok(/notest$/.test($(form).prop('action')), 'Making sure action is not replaced');
equal($(form).prop('target'), '', 'Making sure that target is not replaced');
ok(false === result, 'click event returns false to not refresh page');

Другие 2 находятся в функции отправки, которая не выглядит так, как она вызывается как часть теста.

Помните, что эти тесты являются синхронными, поэтому он не будет ждать, пока вы нажмете submit перед запуском теста и сбоя.

Вот пример

test('asynchronous test', function() {
    setTimeout(function() {
        ok(true);
    }, 100)
})

Не удалось, так как ok запускается через 100 мс после теста.

test('asynchronous test', function() {
    // Pause the test first
    stop();

    setTimeout(function() {
        ok(true);

        // After the assertion has been called,
        // continue the test
        start();
    }, 100)
})

Стоп() сообщает qunit ждать, а start() - идти!

Существует также ayncTest(), подробно описанный в api здесь

Наконец, кажется, что вы пытаетесь отлаживать свой код с помощью этих тестов. Было бы намного проще использовать инструменты разработчика Chrome или firebug в firefox, чтобы установить точки останова на вашем коде, а также использовать console.log() и console.dir() для вывода информации.

Сказав это, я понятия не имею, как это работает для вас вообще, поэтому я мог бы что-то упустить:) Если вы все еще застряли, посмотрите, можете ли вы добавить еще какой-то окружающий код и что вы пытаетесь достичь, Надеюсь, это поможет.

PS: в конце есть также };, который недопустим в коде, который вы нам дали, вероятно, актуальным в самом приложении;)