我在单元测试angularjs应用程序方面非常新,我想我不了解在angularjs上测试基于promise的服务的主要概念。
我将直接从我的例子开始:
我有一个SQLite数据库服务,它有这个方法:
var executeQuery = function(db,query,values,logMessage) {
return $cordovaSQLite.execute(db, query, values).then(function(res) {
if(res.rows.length>0) return res;
else return true;
}, function (err) {
return false;
});
};
我想写一个测试用例,在那里我执行一个查询然后我想得到我的服务的executeQuery函数的返回值。
我的测试说明是这样的:
describe("Test DatabaseCreateService‚", function () {
var DatabaseCreateService,cordovaSQLite,ionicPlatform,rootScope,q;
var db=null;
beforeEach(module("starter.services"));
beforeEach(module("ngCordova"));
beforeEach(module("ionic"));
beforeEach(inject(function (_DatabaseCreateService_, $cordovaSQLite,$ionicPlatform,$rootScope,$q) {
DatabaseCreateService = _DatabaseCreateService_;
cordovaSQLite = $cordovaSQLite;
ionicPlatform = $ionicPlatform;
q = $q;
rootScope = $rootScope;
ionicPlatform.ready(function() {
db = window.openDatabase("cgClientDB-Test.db", '1', 'my', 1024 * 1024 * 100);
});
}));
describe("Test DatabaseCreateService:createTableLocalValues", function() {
it("should check that the createTableLocalValues was called correctly and return correct data", function() {
var deferred = q.defer();
deferred.resolve(true);
spyOn(DatabaseCreateService,'createTableLocalValues').and.returnValue(deferred.promise);
var promise = DatabaseCreateService.createTableLocalValues(db);
expect(DatabaseCreateService.createTableLocalValues).toHaveBeenCalled();
expect(DatabaseCreateService.createTableLocalValues).toHaveBeenCalledWith(db);
expect(DatabaseCreateService.createTableLocalValues.calls.count()).toEqual(1);
promise.then(function(resp) {
expect(resp).not.toBe(undefined);
expect(resp).toBe(true);
},function(err) {
expect(err).not.toBe(true);
});
rootScope.$apply();
});
});
});
此测试说明有效,但它不会返回函数中的值而不是返回deferred.resolve(true);
中已解决的内容
我想要做的是调用DatabaseCreateService.createTableLocalValues函数并解析从函数返回的数据。
createTableLocalValues函数是这样的:
var createTableLocalValues = function(db) {
var query = "CREATE TABLE IF NOT EXISTS `local_values` (" +
"`Key` TEXT PRIMARY KEY NOT NULL," +
"`Value` TEXT );";
return executeQuery(db,query,[],"Create cg_local_values");
};
如果我在浏览器或设备上运行此方法,如果一切正常并且表已创建,我会得到一个真实的回复。那么我如何在测试描述中得到这个真实的真实,而不是像我上面的例子中的假真实?
感谢您提供任何帮助。
示例2(使用callThrough):
describe('my fancy thing', function () {
beforeEach(function() {
spyOn(DatabaseCreateService,'createTableSettings').and.callThrough();
});
it('should be extra fancy', function (done) {
var promise = DatabaseCreateService.createTableSettings(db);
rootScope.$digest();
promise.then(function(resp) {
console.log(resp);
expect(resp).toBeDefined();
expect(resp).toBe(true);
done();
},function(err) {
done();
});
});
});
在karma-runner中记录消息:
LOG: true
Chrome 46.0.2490 (Mac OS X 10.11.1) Test DatabaseCreateService‚ testing createTable functions: my fancy thing should be extra fancy FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Chrome 46.0.2490(Mac OS X 10.11.1):执行42 of 42(1次失败)(8.453秒/ 8.03秒)
更新:
事实证明,这个问题与$ cordovaSQLite.executeQuery函数本身有关。不知怎的,承诺没有超时,这就是错误导致的。我将ng-cordova的执行功能改为此。 (希望这种改变不会破坏任何工作)
execute: function (db, query, binding) {
var q = Q.defer();
db.transaction(function (tx) {
tx.executeSql(query, binding, function (tx, result) {
q.resolve(result);
},
function (transaction, error) {
q.reject(error);
});
});
return q.promise.timeout( 5000, "The execute request took too long to respond." );
}
通过该更改,测试正确传递!
答案 0 :(得分:3)
你可以监视一个函数,并使用
委托实际的实现spyOn(DatabaseCreateService,'createTableLocalValues').and.callThrough();
您可能还需要在致电函数后致电rootScope.$digest()
,以便承诺解决。
修改强>
在测试异步代码时,您应该使用done
模式:
it('should be extra fancy', function (done) {
var promise = DatabaseCreateService.createTableSettings(db);
rootScope.$digest();
promise.then(function(resp) {
console.log(resp);
expect(resp).toBeDefined();
expect(resp).toBe(false);
expect(resp).toBe(true);
done();
});
});
答案 1 :(得分:0)
关于你在测试中断言方式的建议:
在您的测试中,您正在为返回的承诺调用then
以进行断言:
promise.then(function(resp) {
expect(resp).not.toBe(undefined);
expect(resp).toBe(true);
},function(err) {
expect(err).not.toBe(true);
});
这迫使您在错误函数中添加断言,以便在承诺根本无法解决时您的测试仍然失败。
请尝试使用Jasmine Promise Matchers。当您的测试失败时,它将使您的测试代码更易于阅读并导致更清晰的错误消息。你的测试看起来像这样:
expect(promise).toBeResolvedWith(true);