使用单元测试测试jQuery中的document.ready函数

5

我有一个关于单元测试jQuery的document.ready()函数的问题。

目前我的代码中有两个场景:

function myFunction()
{
    $(document).ready(function() { ... });
}

并且:

$(document).ready(function()
{
    // some really long setup code here
});

我试着为第一个场景编写单元测试,但我无法让它运行到document.ready函数中。至于第二个场景,我还没有想出一种测试方法(我在思考如何测试以及语法方面遇到了麻烦)。
所以假设我不能更改源代码,有没有办法测试这些功能呢?(假设测试它们是个好主意)
谢谢。

你到底想要测试什么?只是测试代码,为什么不测试你的代码在做什么呢? - powtac
我正在尝试测试$(document).ready(function() { //test these code });中的逻辑。我正在使用JsTestDriver来测试我的JavaScript。目前我遇到了难以进入document.ready块的困难。 - BeraCim
2个回答

7

您不需要测试$(document).ready,因为它是框架的一部分并已经进行了单元测试。在编写单元测试时,您需要测试两个方面:

  1. 与框架的交互。这包括确保使用正确的参数调用正确的函数。
  2. 您自己的代码-确保您的代码执行正确的操作。

因此,您真正需要做的是确保从$(document).ready中调用的任何代码都是正确的。

function myInit(){
//...
}
function myFunction()
{
  $(document).ready(myInit);
}

现在您需要做的就是对myInit函数进行单元测试。

您还可以模拟$.ready函数,以确保您正在调用它:

var readyCalled = false;
$.ready = function(func){
  readyCalled = (myInit == func);
}

//Your code containing `myInit` will get executed somewhere here
//....
//Then test:
test("Should have called ready", function() {
 ok(readyCalled, "ready should have been called with myInit as a parameter.")
});

1
@Igor:针对你的例子,如果有很多不调用外部函数(如myInit)的逻辑呢?我试着编写单元测试来测试myFunction,但是我无法进入document.ready(语法方面)。我的代码中有很多$(document).ready(function() {});,因此我觉得有必要进行测试。然后有人不太令人信服地告诉我不需要测试document.ready。因此我感到困惑。谢谢。 - BeraCim
@BeraCim 要使用这个解决方案,您可以将那些匿名函数重构为命名函数,这样您就可以从单元测试中访问它们,但这违反了您问题中的一个约束(“假设我不能更改源代码”)。 - jyoungdev

0
注册 on ready 处理程序的函数应该注册另一个函数,而不是匿名代码块。这样,您就可以将调用 $.ready() 的代码与在 ready 上运行的代码分开测试。因此,您有:
1. 一个测试来验证正确的函数被设置为 ready 处理程序 2. 另一个测试来验证 ready 处理程序执行正确的操作
要测试方案 1,您需要为 jQuery 注入一个测试替身。这很困难,因为如果重新定义 $ 或 jQuery,很可能会破坏依赖它进行其他处理(如测试运行器)的其他代码。同时,当您使用实用方法(如数组连接)时,您的代码仍然可能希望直接调用 jQuery。不过,任何控制反转模式都应该解决这个问题(http://martinfowler.com/articles/injection.html)。
无论如何,这里是一些使用构造函数注入的代码(使用 JSMock 作为模拟库,使用 QUnit(jQuery 的一部分)作为测试运行器):
// the code

var createComponent = function(_$) {
    var that = {};

    that.OnStart = function() {
        _$.ready(this.OnReady);
    };

    that.OnReady = function() {
    };

    return that;
};

// the test

test("OnStart associates the ready handler", function() {

   var sut;

   var mock$ = mc.createMock($);
   mock$.expects().ready(isA.TypeOf(Function)).andStub(function(callback) {
        equals(callback, sut.OnReady);
   });

   sut = createComponent(mock$);

   sut.OnStart();

   mc.verify();
});

test("OnReady does the right stuff", function() {
    //etc
});

我在JS中使用这个通用模式来处理所有的事件处理程序... 你可能更喜欢使用原型类型类。当你将函数作为参数传递给jQuery时,需要注意的是,当调用这些回调函数时,jQuery不会设置"this"值。在测试中,这会导致equals(callback, sut.OnReady)不再通过。为了解决这个问题,你需要使事件处理程序成为每个实例的直接成员。你可以想象当有很多实例时,有一个工具可以接受它们的列表,但这演示了使"OnReady"成为一个不依赖于"this"的成员。

var Component = function(_$) {
    this._$ = _$;

    // repeat for each event handler thats tested
    this.OnReady = function() {
        Component.prototype.OnReady.apply(this);
    }
}

Component.prototype.Start = function() {
    this._$.ready(this.OnReady);
}

Component.prototype.OnReady = function() {
}

那么除了将代码移到单独的函数中,没有其他方法可以测试匿名代码块吗? - BeraCim
你可以使用 JSMock 的 andStub() 功能来记录传递进来的回调,然后从同一个测试中调用该回调。然而这并不可取,因为现在你有一个验证分离的测试(导致缺陷定位差),请参考 http://xunitpatterns.com/Goals%20of%20Test%20Automation.html#Defect%20Localization。 - Frank Schwieterman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接