我有以下角度控制器
function IndexCtrl($scope, $http, $cookies) {
//get list of resources
$http.get(wtm.apiServer + '/v1/developers/me?access_token=' + $cookies['wtmdevsid']).
success(function(data, status, headers, config) {
// snip
}).
error(function(data, status, headers, config) {
// snip
});
$scope.modal = function() {
// snip
}
return;
}
我要做的是在$ http服务上模拟get方法。这是我的单元测试代码:
describe('A first test suite', function(){
it("A trivial test", function() {
expect(true).toBe(true);
});
});
describe('Apps', function(){
describe('IndexCtrl', function(){
var scope, ctrl, $httpBackend;
var scope, http, cookies = {wtmdevsid:0};
beforeEach(inject(function($injector, $rootScope, $controller, $http) {
scope = $rootScope.$new();
ctrl = new $controller('IndexCtrl', {$scope: scope, $http: $http, $cookies: cookies});
spyOn($http, 'get');
spyOn(scope, 'modal');
}));
it('should create IndexCtrl', function() {
var quux = scope.modal();
expect(scope.modal).toHaveBeenCalled();
expect($http.get).toHaveBeenCalled();
});
});
});
当我跑步时,我得到了 ReferenceError:未定义wtm。
wtm是一个全局对象,当然我在运行测试时不会定义它,因为我运行测试时没有运行声明它的代码。我想知道的是为什么要调用真正的$ http.get函数,以及如何设置间谍或存根以便我实际上不调用真正的函数?
(inb4讨厌全局变量:我的一个同事的任务是将我们的代码中的因素分解:) :)
答案 0 :(得分:4)
您需要在测试之前连接whenGET
$httpBackend
方法。尝试在“使用模拟后端进行单元测试”下的测试的beforeEach()
函数中进行设置... There is a good example here。
答案 1 :(得分:4)
我建议所有使用此处所述方式的全局变量应通过$window
服务使用。
所有可用的全局变量,例如window.wtm,也将在$ window.atm上提供。
然后你可以完全删除你的wtm引用并以你已经描述的方式监视它:
var element, $window, $rootScope, $compile;
beforeEach(function() {
module('fooApp', function($provide) {
$provide.decorator('$window', function($delegate) {
$delegate.wtm = jasmine.createSpy();
return $delegate;
});
});
inject(function(_$rootScope_, _$compile_, _$window_) {
$window = _$window_;
$rootScope = _$rootScope_;
$compile = _$compile_;
});
});
答案 2 :(得分:1)
也许你可以围绕$ httpBackend创建一个自定义包装器模拟来处理你的特殊需求。
详细说明,Angular会使用最后一次服务策略覆盖同名的组件,这意味着您加载模块的顺序在测试中非常重要。
当您定义另一个具有相同名称的服务并在第一个服务之后加载它时,将注入最后一个服务而不是第一个服务。 E.g:
apptasticMock.service("socket", function($rootScope){
this.events = {};
// Receive Events
this.on = function(eventName, callback){
if(!this.events[eventName]) this.events[eventName] = [];
this.events[eventName].push(callback);
}
// Send Events
this.emit = function(eventName, data, emitCallback){
if(this.events[eventName]){
angular.forEach(this.events[eventName], function(callback){
$rootScope.$apply(function() {
callback(data);
});
});
};
if(emitCallback) emitCallback();
}
});
此服务提供完全相同的界面,其行为与原始界面完全相同,但它从不通过任何套接字进行通信。这是我们想要用于测试的服务。
考虑到角度的加载顺序,测试看起来像这样:
describe("Socket Service", function(){
var socket;
beforeEach(function(){
module('apptastic');
module('apptasticMock');
inject(function($injector) {
socket = $injector.get('socket');
});
});
it("emits and receives messages", function(){
var testReceived = false;
socket.on("test", function(data){
testReceived = true;
});
socket.emit("test", { info: "test" });
expect(testReceived).toBe(true);
});
});
重要的是module('apptasticMock')
在module('apptastic')
之后执行。这会使用模拟的套接字覆盖原始套接字实现。其余的只是正常的依赖注入程序。
This article I wrote对你来说很有意思,因为它会进一步详细说明。