虽然上面的答案可能很好用(我还没有尝试过 :)),但我通常有更多的测试要运行,所以我不会在测试本身中注入。我会将it()用例分组到describe块中,并在每个describe块的beforeEach()或beforeAll()中运行我的注入。
Robert也正确地指出,您必须使用Angular $injector使测试知道服务或工厂。 Angular自己在应用程序中也使用此注入器,以告诉应用程序可用的内容。
但是,它可以在多个位置调用,而且也可以隐式调用而不是显式调用。您会注意到在下面的示例spec测试文件中,beforeEach()块隐式调用注入器,以使事物可用于在测试中分配。
回到分组和使用before-blocks的问题,这里是一个小例子。我正在制作一个Cat Service并希望对其进行测试,因此编写和测试Service的简单设置如下:
app.js
var catsApp = angular.module('catsApp', ['ngMockE2E']);
angular.module('catsApp.mocks', [])
.value('StaticCatsData', function() {
return [{
id: 1,
title: "Commando",
name: "Kitty MeowMeow",
score: 123
}, {
id: 2,
title: "Raw Deal",
name: "Basketpaws",
score: 17
}, {
id: 3,
title: "Predator",
name: "Noseboops",
score: 184
}];
});
catsApp.factory('LoggingService', ['$log', function($log) {
function _parseStuffIntoMessage(stuff) {
var message = "";
if (typeof stuff !== "string") {
message = JSON.stringify(stuff)
} else {
message = stuff;
}
return message;
}
var write = function(stuff) {
var log_msg = _parseStuffIntoMessage(stuff);
$log.log(log_msg);
}
var error = function(stuff) {
var err_msg = _parseStuffIntoMessage(stuff);
$log.error(err_msg);
}
return {
error: error,
write: write
};
}])
catsApp.factory('CatsService', ['$http', 'LoggingService', function($http, Logging) {
var Success_Callback = function(response) {
Logging.write("CatsService::getAllCats()::Success!");
return {"status": status, "data": data};
}
var Error_Callback = function(response) {
Logging.error("CatsService::getAllCats()::Error!");
return {"status": status, "data": data};
}
var allCats = function() {
console.log('# Cats.allCats()');
return $http.get('/cats')
.then(Success_Callback, Error_Callback);
}
return {
getAllCats: allCats
};
}]);
var CatsController = function(Cats, $scope) {
var vm = this;
vm.cats = [];
vm.activate = function() {
console.log('* CatsCtrl.activate()!');
Cats.getAllCats().then(
function(litter) {
console.log('> ', litter);
vm.cats = litter;
console.log('>>> ', vm.cats);
}
);
}
vm.activate();
}
CatsController.$inject = ['CatsService', '$scope'];
catsApp.controller('CatsCtrl', CatsController);
规格:猫控制器
'use strict';
describe('Unit Tests: Cats Controller', function() {
var $scope, $q, deferred, $controller, $rootScope, catsCtrl, mockCatsData, createCatsCtrl;
beforeEach(module('catsApp'));
beforeEach(module('catsApp.mocks'));
var catsServiceMock;
beforeEach(inject(function(_$q_, _$controller_, $injector, StaticCatsData) {
$q = _$q_;
$controller = _$controller_;
deferred = $q.defer();
mockCatsData = StaticCatsData();
catsServiceMock = {
getAllCats: function() {
deferred.resolve(mockCatsData);
return deferred.promise;
}
};
}));
var createCatsController;
beforeEach(inject(function (_$rootScope_, $controller, CatsService) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
createCatsController = function() {
return $controller('CatsCtrl', {
'$scope': $scope,
CatsService: catsServiceMock
});
};
}));
it('should have NO cats loaded at first', function() {
catsCtrl = createCatsController();
expect(catsCtrl.cats).toBeDefined();
expect(catsCtrl.cats.length).toEqual(0);
});
it('should call "activate()" on load, but only once', function() {
catsCtrl = createCatsController();
spyOn(catsCtrl, 'activate').and.returnValue(mockCatsData);
catsCtrl.activate();
$rootScope.$digest();
expect(catsCtrl.activate).toBeDefined();
expect(catsCtrl.activate).toHaveBeenCalled();
expect(catsCtrl.activate.calls.count()).toEqual(1);
expect(catsCtrl.cats.length).toBeGreaterThan(0);
});
it('each cat will have a NAME, TITLE and SCORE', function() {
catsCtrl = createCatsController();
spyOn(catsCtrl, 'activate').and.returnValue(mockCatsData);
catsCtrl.activate();
$rootScope.$digest();
var names = _.map(catsCtrl.cats, function(cat) { return cat.name; })
var titles = _.map(catsCtrl.cats, function(cat) { return cat.title; })
var scores = _.map(catsCtrl.cats, function(cat) { return cat.score; })
expect(names.length).toEqual(3);
expect(titles.length).toEqual(3);
expect(scores.length).toEqual(3);
});
});
规范:猫服务
'use strict';
describe('Unit Tests: Cats Service', function() {
var $scope, $rootScope, $log, cats, logging, $httpBackend, mockCatsData;
beforeEach(module('catsApp'));
beforeEach(module('catsApp.mocks'));
describe('has a method: getAllCats() that', function() {
beforeEach(inject(function($q, _$rootScope_, _$httpBackend_, _$log_, $injector, StaticCatsData) {
cats = $injector.get('CatsService');
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
mockCatsData = StaticCatsData();
var deferred = $q.defer();
deferred.resolve(mockCatsData);
spyOn(cats, 'getAllCats').and.returnValue(deferred.promise);
$httpBackend.whenGET('/cats').respond(200, mockCatsData);
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
})
it(' exists/is defined', function() {
expect( cats.getAllCats ).toBeDefined();
expect( typeof cats.getAllCats ).toEqual("function");
});
it(' returns an array of Cats, where each cat has a NAME, TITLE and SCORE', function() {
cats.getAllCats().then(function(data) {
var names = _.map(data, function(cat) { return cat.name; })
var titles = _.map(data, function(cat) { return cat.title; })
var scores = _.map(data, function(cat) { return cat.score; })
expect(names.length).toEqual(3);
expect(titles.length).toEqual(3);
expect(scores.length).toEqual(3);
})
});
})
describe('has a method: getAllCats() that also logs', function() {
var cats, $log, logging;
beforeEach(inject(
function(_$log_, $injector) {
cats = $injector.get('CatsService');
$log = _$log_;
logging = $injector.get('LoggingService');
spyOn(cats, 'getAllCats').and.callThrough();
}
))
it('that on SUCCESS, $logs to the console a success message', function() {
cats.getAllCats().then(function(data) {
expect(logging.write).toHaveBeenCalled();
expect( $log.log.logs ).toContain(["CatsService::getAllCats()::Success!"]);
})
});
})
});
编辑
根据一些评论,我更新了我的答案,使其稍微复杂一些,并且还创建了一个Plunkr演示单元测试。
具体而言,其中一条评论提到“如果控制器的服务本身有简单的依赖关系,例如$log?”-这在带有测试用例的示例中已经包含了。
希望能帮助您!测试或黑客星球!!!
https://embed.plnkr.co/aSPHnr/
beforeEach
中注入服务?正在测试一个数据模型(服务),它包含大量全局变量。谢谢,C§ - CSS