【问题标题】:How do I unit test a $(function () { ... }) block with Sinon and QUnit?如何使用 Sinon 和 QUnit 对 $(function () { ... }) 块进行单元测试?
【发布时间】:2012-11-25 02:48:41
【问题描述】:

我在一个 JS 文件中有这样的代码(当然是简化的):

$(function () {
  var num;
  $.getJSON('./getNumber.php', function (n) {
    num = n;
  });

  $('#id').on('click', function () { alert(num); });
});

我需要编写两个单元测试:

  1. 确保脚本在页面加载时向服务器发送./getNumber.php 请求
  2. 确保在单击#id 时,num 会收到警报。

显然我需要 FakeXmlHTTPRequest 并模拟 alert 函数,甚至可能监视 $.getJSON。但是我不确定在保持两个原子的同时编写测试的正确方法是什么。

我认为这样做的唯一方法是为每个测试动态注入 <script> 块;但我只是觉得那是不对的。什么是正确的方法?谢谢。

编辑:基于 SO 之外的 cmets,我需要学习的是编写可测试的 Javascript,而不是尝试为低可测试性的东西提出测试用例。如果有人可以给我一些关于重写此代码的建议,我将不胜感激。

【问题讨论】:

    标签: jquery unit-testing qunit sinon


    【解决方案1】:

    您的问题没有一个正确的答案,而是有很多值得学习的好原则。考虑以下代码。我已经重构了你的例子。我将在下面解释我所做的一切。

    // Define your module in a self-executing function.
    // This module is now completely unit testable.
    var myModule = (function () {
        var num;
    
        function getNum() {
            $.getJSON('./getNumber.php', function (n) {
                num = n;
            });
        }
    
        function alertNum() {
            alert(num);
        }
    
        // this returned object will by set to myModule and will publicly
        // expose the necessary functions
        return {
            getNum: getNum,
            alertNum: alertNum
        };
    })();
    
    // wire up your events
    $(function() {
        $("#id").on('click', myModule.alertNum);
    });
    

    这个例子对于这个简单的代码来说显然是多余的,但它应该给你一些好主意。

    1. 您的核心功能现在封装在一个模块中。您在模块中保留了额外的私有函数和变量(如num),但现在可以测试通过返回对象公开公开的所有内容。 (见下文注释)

    2. 我已经连接了模块外的事件。如果您的模块很大,您甚至可能希望将此代码放在另一个脚本中。无论哪种方式,它都会以更类似于 MVC 的方法分离您的关注点。您可以测试这段代码,但实际上不必这样做;它应该是非常简单的 jQuery,围绕它进行包装测试只不过是测试 jQuery 库,它已经被 jQuery 团队彻底测试过。

    3. 这可能看起来很奇怪,但不要在模块中使用任何 jQuery 选择器!如果您的模块需要一个元素,请将其作为函数参数传入。有一些方法可以将这些东西排除在外,但这确实很麻烦,如果你已经正确地分离了你的关注点,那就没有必要了。我的大部分模块(或其中的对象)都以init() 方法结束,我可以在其中传递东西。 -- 您可能仍然选择在模块内的元素上使用 jQuery 的 .find() 方法。这更像是一个判断电话,只有您可以根据您的模块的特定需求做出判断。这样做将需要您在测试中对 DOM 元素进行更详细的模拟,但有时这比将数十个元素传递到 init() (只需传递主容器)更容易。


    注意:我强烈建议使用RequireJS 或其他 AMD 模块加载器来管理您的模块。这将使上面的 myModule 之类的内容远离全局命名空间。不过,使用 QUnit 设置 Require 有点小题大做,一旦您掌握了单元测试,这可能是一个稍后需要解决的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-21
      • 2021-03-15
      • 1970-01-01
      • 2021-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多