使用QUnit对AJAX请求进行单元测试 [英] Unit testing AJAX requests with QUnit

查看:119
本文介绍了使用QUnit对AJAX请求进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We are trying to implement QUnit JavaScript tests for a JS-heavy web app. We are struggling to find a way to successfully test methods that involve jQuery AJAX requests. For example, we have the following constructor function (obviously this is a very simplistic example):

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 方法,最好使用存根URL而不是真正的 someURL.php

We are trying to find a way to test the fire method, preferably with a stubbed URL instead of the real someURL.php.

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

The only obvious solution to me at the moment is add the URL, and the success callback, as arguments to the constructor function. That way, in the test, we can create a new instance of X and pass in the stub URL, and a callback to run when the stub returns a response. For example:

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();
    });
});

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

However, this doesn't seem like a very nice solution. Is there a better way?

推荐答案

使用jQuery,你可以使用 .ajax的xhr对象( )作为承诺返回,因此您可以添加更多处理程序(见下文),而不仅仅是单个成功完成错误您在选项中定义的那些。因此,如果你的异步函数可以返回xhr对象,你可以添加特定于测试的处理程序。

With jQuery, you can use the xhr object that .ajax() returns as a promise, so you can add more handlers (see below) than just the single success, complete and error ones you define in the options. So if your async function can return the xhr object, you can add test-specific handlers.

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

As for the URL, that's a little trickier. I've sometimes set up a very simple Node server on localhost, which just serves canned responses that were copied from the real server. If you run your test suite off that same server, your URLs just need to be absolute paths to hit the test server instead of the production server. And you also get a record of the requests themselves, as a server sees them. Or you can have the test server send back errors or bad responses on purpose, if you want to see how the code handles it.

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

But that's of course a pretty complex solution. The easier one would be to define your URLs in a place where you can redefine them from the test suite. For instance:

/* 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

此外,QUnit还有 asyncTest function,为你调用 stop()。添加一个小帮手来跟踪何时重新开始,你有一个非常好的解决方案。

Also, QUnit has an asyncTest function, which calls stop() for you. Add a tiny helper to keep track of when to start again, and you've got a pretty good solution.

这是我之前做过的事情

// 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 是一个倒计时的功能从你指定的任何内容为零,然后调用 start()。在这种情况下,有1个异步调用,因此 countDown 将从中倒数。当ajax调用结束时,无论它如何运行,它都会这样做,因为它被设置为总是回调。

因为 asyncTest 被告知需要2个断言,如果从未调用 .done()回调,它将报告错误因为没有断言会被运行。因此,如果通话完全失败,您也会知道。如果你想记录错误的东西,可以在promise链中添加一个 .fail()回调。

Basically countDown is a function that counts down to zero from whatever you specify, and then calls start(). In this case, there's 1 async call, so countDown will count down from that. And it'll do so when the ajax call finishes, regardless of how it went, since it's set up as an always callback.
And because the asyncTest is told to expect 2 assertions, it'll report an error if the .done() callback is never called, since no assertions will be run. So if the call completely fails, you'll know that too. If you want to log something on error, you can add a .fail() callback to the promise chain.

这篇关于使用QUnit对AJAX请求进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆