【问题标题】:Qunit test alternates between pass and fail on page refreshQunit 测试在页面刷新时在通过和失败之间交替
【发布时间】:2013-05-23 22:29:37
【问题描述】:

我有两个相互影响的测试。我理解为什么我要替换在第二个测试中内部调用的 jQuery 内置函数。但是我不明白为什么测试交替通过和失败。

This question is similar 但是,我没有直接在 qunit-fixture div 上做任何事情。

这是我的测试

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 也会在页面上产生相同的行为。

在失败的情况下,测试失败是因为在设置 submit() 处理程序时,内部 jQuery 正在调用 .on()。但是为什么在这种情况下测试并不总是失败?在页面刷新期间保持状态的浏览器在做什么?

更新:

这是正在测试的代码:

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;
}();

【问题讨论】:

  • 我认为首先需要更清楚。请检查代码。 getReport 函数也是线索。请显示getReport函数。
  • 添加了正在测试的代码。

标签: javascript jquery unit-testing qunit


【解决方案1】:

如果没有更多信息,我不确定能否解决此问题,但我可以指出一些可能的问题。

第一个测试在第 2 行似乎有无效语法

var panelId = '#PanelMyTab');

但这可能是类型错误,正如你所说,第一个总是通过。

我假设要通过第一个测试(并且有效),loadTab(event,ui) 必须运行 jQuery.fn.on(),否则不会运行任何断言。使用 jQuery UI Tabs 进行一些测试,似乎就是这种情况(只是不确定这是否是您的意图)。

我不确定将这些断言放在该函数中是否可取,并且您必须了解您已经使用不执行任何操作的函数覆盖了 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 个在提交函数中,看起来不像是作为测试的一部分调用的。

请记住,这些测试是同步的,因此它不会在运行测试和失败之前等待您点击提交。

这是一个例子

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)
})

stop() 告诉 qunit 等待, start() 开始!

apihere中还有asyncTest()详解

最后,您似乎正在尝试使用这些测试来调试您的代码。使用chrome开发工具或firefox中的firebug在代码上设置断点,并使用console.log()和console.dir()输出信息会容易得多。

话虽如此,我完全不知道它对你有什么作用,所以我可能会遗漏一些东西 :) 如果你仍然卡住,看看你是否可以添加更多的周围代码以及你想要实现的目标.希望这会有所帮助。

PS:最后还有一个};,这在您给我们的代码中是无效的,但可能与实际应用相关;)

【讨论】:

  • 任何语法错误都是拼写错误。我试图只显示代码的相关部分。不,我不想用这些测试进行调试。这些测试用于测试特定事件,此时同步似乎不是问题。测试按我预期的一半时间执行,但是当我刷新浏览器时,结果会发生变化。这就是我的问题。
  • 你能解释一下如何在第二个测试中获得 5 个断言,而只有 3 个看起来是同步运行的吗?这可能是问题的原因。我为第二个测试 here 模拟了一个小提琴并删除了 getReport 函数。一定是 getReport() 调用通过调用 jQuery.fn.on() 生成断言,我们有没有机会看到该函数的作用?
  • 整个函数调用是同步完成的。我更新了代码以显示正在调用的函数。该函数对表单进行深拷贝,更改一些参数并触发提交事件。这被我的模拟抓住了。问题是测试将通过,当我刷新页面时测试将失败。然后再次刷新页面。每次按 F5 交替显示结果。
  • 我认为这里没有进行任何异步测试。更简单地说,当他重新运行测试时,它会在一次运行中通过,然后在下一次运行中失败。然后它会在每次运行的通过和失败之间交替。
  • 是的,现在才看到更新,抱歉。我理解刷新问题,但它可能是与时间相关的问题。我已经更新了小提琴here,您可以在它自己的页面here 上查看它。在 IE、Chrome 和 firefox 中它可以顺利运行。我不得不编写自己的 jQuery.fn.on(),因为我不知道您的代码中发生了什么,但希望它可以帮助您缩小范围。
【解决方案2】:

我修改了@Ben 的fiddle 以将您的代码包含在您的两个测试中。我修改了您的一些代码以使其正常运行。当您点击运行按钮时,所有测试都将通过。当你再次点击运行按钮时,第二个测试(“交替通过和失败”)将失败——这基本上是在模拟你原来的问题。

问题是您的第一个测试(“始终通过测试”)更改全局状态,方法是将 jQuery.fn.on 函数替换为已覆盖的函数。因此,当测试按顺序运行时,第二个测试(“交替通过和失败”)使用不正确的覆盖 jQuery.fn.on 函数并失败。 每个单元测试都应该将全局状态返回到其测试前状态,以便其他测试可以基于相同的假设运行。

它在通过和失败之间交替的原因是,在引擎盖下 QUnit 总是首先运行失败的测试(它通过 cookie 或本地存储以某种方式记住这一点,我不太确定)。当它首先运行失败的测试时,第二个测试会在第一个测试之前运行;结果,第二个测试获得了 jQuery 的原生 on 函数并且可以工作。当您第三次运行它时,测试将按其“原始”顺序运行,第二次测试将使用覆盖的 on 函数并失败。

这是有效的fiddle。通过缓存原始var jQueryOn = jQuery.fn.on; 函数并在测试结束时通过jQuery.fn.on = jQueryOn; 重置它,我已将修复添加到测试后“取消覆盖”on 函数。您可以改用 QUnit 的模块 teardown() 方法更好地实现这一点。

您可以查看https://github.com/jquery/qunit/issues/74 了解更多信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-06
    • 1970-01-01
    • 2010-10-30
    • 2020-05-04
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    相关资源
    最近更新 更多