【发布时间】:2016-10-06 11:08:28
【问题描述】:
我的问题:在 Karma 中,我在测试实际服务时模拟了注入服务。模拟服务获取一些数据,并返回一个承诺。我无法弄清楚我做错了什么。
我知道实际的“登录”机制存在许多问题,但我用它来说明这个 Karma 问题。我不打算将其用作生产代码。 (但是,欢迎提出更好地说明问题的任何建议!)
首先,我把它写在一个通用的 .js 文件中,并说“node testcode.js”
testcode.js:
function Login(name,password,callback){
setTimeout(function(){
var response;
var promise = getByUserName();
promise.then(successCb);
function successCb(userObj){
if ( userObj != null && userObj.password === password ) {
response = { success : true };
} else {
response = { success: false, message: 'Username or password is incorrect' };
}
callback(response);
};
},200);
}
function getByUserName(){
return Promise.resolve(user);
}
var user = {
username : 'test',
id : 'testId',
password : 'test'
};
var test = undefined;
Login(user.username,user.password,testCb);
function testCb(response){
test = response;
console.log("Final: " + JSON.stringify(test));
}
这给了我预期的结果:
Final: {"success":true}
现在,我尝试在 Karma 中重复这一点......
测试服务:
(function(){
"use strict";
angular.module('TestModule').service('TestService',TestService);
TestService.$inject = ['$http','$cookieStore','$rootScope','$timeout','RealService'];
})();
function TestService($http,$cookieStore,$rootScope,$timeout,RealService){
var service = {};
service.Login = Login;
/* more stuff */
return service;
function Login(username,password,callback){
$timeout(function () {
var response;
var promise = UserService.GetByUsername(username);
promise.then(successCb);
function successCb(user){
if (user !== null && user.password === password) {
response = { success: true };
} else {
response = { success: false, message: 'Username or password is incorrect' };
}
callback(response);
};
}, 1000);
}
}
我的因果报应测试:
describe('TestModule',function(){
beforeEach(module('TestModule'));
describe('TestService',function(){
var service,$rootScope,
user = {
username : 'test',
id : 'testId',
password : 'test'
};
function MockService() {
return {
GetByUsername : function() {
return Promise.resolve(user);
}
};
};
beforeEach(function(){
module(function($provide){
$provide.service('RealService',MockService);
});
inject(['$rootScope','TestService',
function($rs,ts){
$rootScope = $rs;
service = ts;
}]);
});
/**
* ERROR: To the best of my knowledge, this should not pass
*/
it('should Login',function(){
expect(service).toBeDefined();
var answer = {"success":true};
service.Login(user.username,user.password,testCb);
//$rootScope.$apply(); <-- DID NOTHING -->
//$rootScope.$digest(); <-- DID NOTHING -->
function testCb(response){
console.log("I'm never called");
expect(response.success).toBe(false);
expect(true).toBe(false);
};
});
});
});
为什么承诺没有得到解决?我已经尝试根据我在 SO 上阅读的类似问题来搞乱 $rootScope.$digest() ,但似乎没有任何东西可以调用 testCb。
【问题讨论】:
-
你在哪里打电话给
$rootScope.$digest()?它不在代码中。承诺链被回调污染的真正原因是什么?getByUserName是有意还是无意返回原生承诺而不是$q? -
$rootScope.$digest() - 我把它放在代码的各个地方,但它从未对结果产生任何影响。所以,我目前没有它在那里。坦率地说,我不知道如何使用它。 getByUsername 有意返回本机承诺。坦率地说,我真的不明白如何使用 $q。读完这篇 codelord.net/2015/09/24/$q-dot-defer-youre-doing-it-wrong 的文章后,我只是举起双手,想着再节省 $q 一天。
-
我已经从事 JS 工作几年了,Angular 工作了 6 个月。我觉得我已经掌握了基础知识,但是像 $digest 和 $q 这样的东西教会了我其他方面。业力和单元测试指令也几乎摧毁了我的灵魂(请参阅我关于业力指令的其他问题)。
-
$q 承诺是同步的。本机承诺是异步的。 $timeout 是同步的(它应该做
$timeout.flush(),在这种情况下没有这样做)。 setTimeout 是异步的(可以用jasmine.clock处理,现在不是)。应该在 Jasmine 中相应地处理异步规范(它们现在不是)。如果可以在 Angular 应用程序中专门使用 $q 和 $timeout,则应该这样做。我猜你这里有太多未知的概念,应该一一理清。 -
@estus - 谢谢。听起来我需要打破这个烂摊子。我正在测试的模块做我想要的(它是我的应用程序的模拟登录,直到我的服务器端获得授权)。这就是为什么我没有清除您说明的 $timeout 和 $q 问题。
标签: javascript angularjs unit-testing karma-jasmine