使用QUnit对AJAX请求进行单元测试

时间:2012-02-24 13:31:27

标签: javascript unit-testing qunit

我们正在尝试为JS重型Web应用程序实施QUnit JavaScript测试。我们正在努力寻找成功测试涉及jQuery AJAX请求的方法的方法。例如,我们有以下构造函数(显然这是一个非常简单的例子):

var X = function() {
    this.fire = function() {
        $.ajax("someURL.php", {
            data: {
                userId: "james"
            },
            dataType: "json",
            success: function(data) {
                //Do stuff
            }
        });
    };
};
var myX = new X();
myX.fire();

我们正在尝试找到一种方法来测试fire方法,最好使用存根网址而不是真实的someURL.php

目前唯一明显的解决方案是将URL和success回调添加为构造函数的参数。这样,在测试中,我们可以创建X的新实例并传入存根URL,并在存根返回响应时运行回调。例如:

test("Test AJAX function", function() {
    stop();
    var myX = new X();
    //Call the AJAX function, passing in the stub URL and success callback
    myX.fire("stub.php", function(data) {
        console.log(data);
        start();
    });
});

然而,这似乎不是一个非常好的解决方案。还有更好的方法吗?

3 个答案:

答案 0 :(得分:9)

使用jQuery,您可以使用.ajax()作为承诺返回的xhr对象,因此您可以添加更多处理程序(请参阅下文),而不仅仅是单successcomplete和{ {1}}您在选项中定义的内容。因此,如果异步函数可以返回xhr对象,则可以添加特定于测试的处理程序。

至于URL,这有点棘手。我有时在localhost上设置一个非常简单的节点服务器,它只提供从真实服务器复制的预设响应。如果您在相同的服务器上运行测试套件,则您的URL只需要是命中测试服务器而不是生产服务器的绝对路径。当服务器看到它们时,您还会获得请求本身的记录。或者,如果您想查看代码如何处理它,您可以让测试服务器故意发回错误或错误响应。

但这当然是一个非常复杂的解决方案。更容易的是在您可以从测试套件重新定义URL的位置定义URL。例如:

error

此外,QUnit还有一个/* in your code */ var X = function () { this.fire = function () { return $.ajax({ url: this.constructor.url, ... }); }; }; X.url = "someURL.php"; // the production url /* in your tests */ X.url = "stub.php"; // redefine to the test url 功能,可以为您调用asyncTest。添加一个小帮手来跟踪何时重新开始,你有一个很好的解决方案。

这是我之前做过的事情

stop()

基本上// create a function that counts down to `start()` function createAsyncCounter(count) { count = count || 1; // count defaults to 1 return function () { --count || start(); }; } // .... // an async test that expects 2 assertions asyncTest("testing something asynchronous", 2, function() { var countDown = createAsyncCounter(1), // the number of async calls in this test x = new X; // A `done` callback is the same as adding a `success` handler // in the ajax options. It's called after the "real" success handler. // I'm assuming here, that `fire()` returns the xhr object x.fire().done(function(data, status, jqXHR) { ok(data.ok); equal(data.value, "foobar"); }).always(countDown); // call `countDown` regardless of success/error }); 是一个从您指定的任何内容倒计数到零的函数,然后调用countDown。在这种情况下,有1个异步调用,因此start()将从中倒数。当ajax调用结束时,无论它如何运行,它都会这样做,因为它被设置为countDown回调。 并且因为always被告知期望2个断言,如果永远不会调用asyncTest回调,它将报告错误,因为不会运行断言。因此,如果通话完全失败,您也会知道。如果您想记录错误的内容,可以在promise链中添加.done()回调。

答案 1 :(得分:3)

如果它是可以(并且应该)与服务器端隔离运行的单元测试,则可以简单地“替换”$.ajax来模拟任何行为。 一个简单的例子:

test("Test AJAX function", function() {
  // keep the real $.ajax
  var _real_ajax = $.ajax;

  // Simulate a successful response
  $.ajax = function(url, opts) {
    opts.success({expected: 'response'});
  }

  var myX = new X();
  // Call your ajax function
  myX.fire();
  // ... and perform your tests

  // Don't forgot to restore $.ajax!
  $.ajax = _real_ajax;
});

显然你也可以使用存根url / data执行真正的ajax调用:

// Simulate a successfully response
$.ajax = function(url, opts) {
  opts.success = function(data) { 
    console.log(data);
    start();
  }
  _real_ajax('stub.php', opts)
}

如果您没有复杂的回答,我更喜欢第一种方法,因为它更快更容易理解 但是,您也可以采用另一种方式将Ajax逻辑放在自己的方法中,这样您就可以在测试期间轻松将其存根。

答案 2 :(得分:2)

尝试使用jQuery spy。我已经创建了一种更简单的方法来使用jQuery熟悉的语法来测试ajax。

Check this link