使用QUnit单元测试AJAX请求
我们正在尝试为JS沉重的Web应用程序实施QUnit JavaScript测试。我们正在努力寻找一种方法来成功测试涉及jQuery AJAX请求的方法。例如,我们有以下的构造函数(显然这是一个非常简单的例子):使用QUnit单元测试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
方法,最好用存根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();
});
});
但是,这似乎不是一个很好的解决方案。有没有更好的办法?
使用jQuery,您可以使用XHR对象.ajax()
回报的承诺,这样你就可以添加更多的处理程序(见下文),不只是你的选项定义了单一success
,complete
和error
的。所以如果你的异步函数可以返回xhr对象,你可以添加特定于测试的处理程序。
至于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
此外,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
});
基本上countDown
做的是,将递减至零,从任何你指定,然后调用start()
的功能。在这种情况下,有1个异步调用,所以countDown
将从此处倒数。当ajax调用完成时它会这样做,无论它如何发生,因为它被设置为always
回调。
而且因为asyncTest
被告知需要2个断言,所以如果.done()
回调从未被调用,它将报告错误,因为没有断言将被运行。所以如果通话完全失败,你也会知道。如果您想记录错误信息,可以将.fail()
回调添加到承诺链中。
如果它是可以(也应该)被孤立于服务器端运行单元测试,你可以简单地“代替” $.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 /数据进行真正的Ajax调用:
// Simulate a successfully response
$.ajax = function(url, opts) {
opts.success = function(data) {
console.log(data);
start();
}
_real_ajax('stub.php', opts)
}
如果您还没有一个复杂的反应,我更喜欢第一种方法,因为它更快更容易理解。
但是,您也可以采取另一种方式,并将Ajax逻辑放入其自己的方法中,以便在测试期间轻松存根。
为了从服务器端隔离我的测试,我用这个库取得了很大的成功https://github.com/jakerella/jquery-mockjax – 2014-12-15 19:23:27
这太好了。非常感谢:) – 2012-02-24 15:27:13