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

Jam*_*ice 6 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();
Run Code Online (Sandbox Code Playgroud)

我们正试图找到一种方法来测试该fire方法,最好是使用存根URL而不是真实的URL 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();
    });
});
Run Code Online (Sandbox Code Playgroud)

但是,这似乎不是一个非常好的解决方案.有没有更好的办法?

Fla*_*ino 9

使用jQuery,您可以使用.ajax()作为promise返回的xhr对象,因此您可以添加更多处理程序(请参见下文),而不仅仅是单个success,complete以及error您在选项中定义的处理程序.因此,如果异步函数可以返回xhr对象,则可以添加特定于测试的处理程序.

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

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

/* 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
Run Code Online (Sandbox Code Playgroud)

此外,QUnit有一个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
});
Run Code Online (Sandbox Code Playgroud)

基本上countDown是一个函数,从你指定的任何东西倒数到零,然后调用start().在这种情况下,有1个异步调用,因此countDown将从中倒数.当ajax调用完成时,无论它如何运行,它都会这样做,因为它被设置为always回调.
并且因为asyncTest被告知期望2个断言,如果.done()从不调用回调,它将报告错误,因为不会运行断言.因此,如果通话完全失败,您也会知道.如果要记录错误的内容,可以.fail()向promise链添加回调.