【问题标题】:How do I make QUnit block until a module is complete?如何让 QUnit 阻塞直到模块完成?
【发布时间】:2013-05-02 22:59:33
【问题描述】:

我正在尝试使用 QUnit 来测试一堆 javascript。我的代码如下所示:

module("A");
doExpensiveSetupForModuleA();
asyncTest("A.1", testA1);
asyncTest("A.2", testA3);
asyncTest("A.3", testA3);

module("B");
doExpensiveSetupForModuleB();
asyncTest("B.1", testB1);
asyncTest("B.2", testB3);
asyncTest("B.3", testB3);

如果我按原样运行此程序,则 doExpensiveSetupForModuleB() 在异步测试运行时运行,导致失败。

如果doExpensiveSetupForModuleB()testA* 之前运行,那么这些测试将失败或撤消昂贵的设置工作,从而导致testB* 失败。

有没有办法在下一个模块上设置 QUnit 块?或者让它阻止开始一个新的测试,直到之前的异步测试完成?或者我应该使用更好的 JS 测试框架吗?

注意:我知道我的单元测试不是完全原子的。我确实有清理代码,可以帮助确保我没有得到任何脏状态,但doExpensiveSetupFor*() 的成本非常高,因此在每次测试之前运行它是不现实的。

【问题讨论】:

    标签: javascript unit-testing qunit


    【解决方案1】:

    你能用module lifecycle吗?

    function runOnlyOnce(fn) {
        return function () {
            try {
                if (!fn.executed) {
                    fn.apply(this, arguments);
                }
            } finally {
                fn.executed = true;
            }
        }
    }
    
    // http://api.qunitjs.com/module/
    module("B", {
        setup: runOnlyOnce(doExpensiveSetupForModuleB)
    });
    

    【讨论】:

    • 理论上我认为我可以做到。我遇到了这种奇怪的情况,我的所有测试都是按顺序列出的,但它们实际上是以相反的顺序执行的。我可以解决这个问题,但我会畏缩等待订单意外反转的那一天。
    • 好建议,这是另一个。你不需要添加魔法属性fn.executed,你可以在runOnlyOnce使用一个局部变量看stackoverflow.com/questions/3332265/…可能没关系,但是你应该遵循最小权限的原则,不要修改你的东西没有创建,因为在这种情况下可能会发生名称冲突。
    • @Hounshell 你的测试应该是原子的,它们不应该依赖于它们运行的​​顺序,QUnit 可以以任何顺序运行你的测试,默认是首先运行失败的测试,你可以改变它但是QUnit 不建议这样做,他们的建议是修复您的测试,因为如果测试不是原子的,它就不是真正的单元测试。寻找 QUnit.config.reorder api.qunitjs.com/QUnit.config
    【解决方案2】:

    这是一个示例,改编自您的原始代码,它为每个测试方法执行 setup 方法:

    function doExpensiveSetupForModuleA() {
        console.log("setup A");
    }
    
    function testA1() {
        console.log("testA1");
        start();
    }
    
    function testA2() {
        console.log("testA2");
        start();
    }
    
    function testA3() {
        console.log("testA3");
        start();
    }
    
    function doExpensiveSetupForModuleB() {
        console.log("setup B");
    }
    
    function testB1() {
        console.log("testB1");
        start();
    }
    
    function testB2() {
        console.log("testB2");
        start();
    }
    
    function testB3() {
        console.log("testB3");
        start();
    }
    
    QUnit.module("A", { setup: doExpensiveSetupForModuleA });
        asyncTest("A.1", testA1);
        asyncTest("A.2", testA2);
        asyncTest("A.3", testA3);
    
    QUnit.module("B", { setup: doExpensiveSetupForModuleB });
        asyncTest("B.1", testB1);
        asyncTest("B.2", testB2);
        asyncTest("B.3", testB3);
    

    这将独立于执行测试的顺序,也独立于每个方法终止所花费的时间。

    对 start() 的调用将确保仅在方法的该点收集测试结果。

    可以在 QUnit Cookbook 中找到更详细的示例: http://qunitjs.com/cookbook/#asynchronous-callbacks

    更新: 如果您不希望在每个测试方法之前执行昂贵的方法,但实际上每个模块只执行一次,只需在代码中添加控制变量以检查模块是否已设置:

    var moduleAsetUp = false;
    var moduleBsetUp = false;
    
    function doExpensiveSetupForModuleA() {
        if (!moduleAsetUp) {
            console.log("setting up module A");
            moduleAsetUp = true;
        }
    }
    
    ...
    
    function doExpensiveSetupForModuleB() {
        if (!moduleBsetUp) {
            console.log("setting up module B");
            moduleBsetUp = true;
        }
    }
    ...
    

    在此示例中,输出将是:

    setting up module A 
     testA1 
     testA2 
     testA3 
     setting up module B 
     testB1 
     testB2 
     testB3 
    

    这样,您将使用昂贵的方法作为模块设置而不是测试方法设置。

    单元测试应该是原子的、独立的、隔离的,因此它们的运行顺序不应该是相关的。

    Qunit 并不总是以相同的顺序运行测试,无论如何,如果你希望你的测试以特定的顺序运行,你可以告诉 QUnit 不要重新排序它们:

    QUnit.config.reorder = false;
    

    这样可以确保 testA 在 testB 之前运行。

    【讨论】:

    • 对不起,我提到的昂贵的设置需要在整个模块之前完成一次,而不是在每次测试之前。将其视为 moduleSetup,而不是 testSetup。
    • 我用如何将该方法用作 moduleSetup 的示例更新了答案。
    • 好的,那么如何确保模块A中的测试在模块B之前完成呢?通常,当我执行测试时,它们会以相反的顺序运行。
    • 更新了关于不重新排序测试的详细信息。
    【解决方案3】:

    我认为您对测试声明的工作方式存在误解。

    QUnit 可以独立运行任何测试。仅仅因为您使用 test() 或 asyncTest() 声明测试并不意味着 QUnit 将调用传入的函数。每个测试旁边的“重新运行”链接重新加载页面并跳过除特定测试之外的每个测试。

    因此,如果您想重新运行 B 模块测试,您的代码将设置 A 模块,即使它不需要。

    其他人发布的模块设置解决方案可能是这里的路。

    【讨论】:

      猜你喜欢
      • 2021-07-30
      • 2010-12-04
      • 2010-09-20
      • 2015-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多