使用 Jasmine 测试 Angular Controller 对 Services with Promise 调用的最佳方式 return
Best way to use Jasmine to test Angular Controller calls to Services with Promise return
在找了一个好的一周后answer/sample,我决定post我的问题。
我需要知道如何编写和测试这样的代码的最佳方式:
控制器
// my.controller.js
(function () {
'use strict';
angular.module('myApp.myModule').controller('Awesome', Awesome);
function Awesome($http, $state, AwesomeService) {
var vm = this; // using 'controllerAs' style
vm.init = init;
vm.awesomeThingToDo = awesomeThingToDo;
vm.init();
function awesomeThingToDo() {
AwesomeService.awesomeThingToDo().then(function (data) {
vm.awesomeMessage = data.awesomeMessage;
});
}
function init() {
vm.awesomeThingToDo(); // Should be ready on page start
}
}
})();
服务
// my.service.js
(function () {
'use strict';
angular.module('myApp.myModule').factory('AwesomeService', AwesomeService);
function AwesomeService($resource, $http) {
var service = {
awesomeThingToDo: awesomeThingToDo
}
return service;
function awesomeThingToDo() {
var promise = $http.get("/my-backend/api/awesome").then(function (response) {
return response.data;
});
return promise;
}
}
})();
我的应用程序适用于此结构。我的服务单元测试也很好。
但是我不知道如何在Controller上做单元测试。
我试过这样的事情:
规格
// my.controller.spec.js
(function () {
'use strict';
describe("Awesome Controller Tests", function() {
beforeEach(module('myApp.myModule'));
var vm, awesomeServiceMock;
beforeEach(function () {
awesomeServiceMock = { Is this a good (or the best) way to mock the service?
awesomeThingToDo: function() {
return {
then: function() {}
}
}
};
});
beforeEach(inject(function ($controller) {
vm = $controller('Awesome', {AwesomeService : awesomeServiceMock});
}));
it("Should return an awesome message", function () {
// I don't know another way do to it... :(
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function() {
return {
then: function() {
vm.awesomeMessage = 'It is awesome!'; // <-- I think I shouldn't do this.
}
}
});
vm.awesomeThingToDo(); // Call to real controller method which should call the mock service method.
expect(vm.awesomeMessage).toEqual('It is awesome!'); // It works. But ONLY because I wrote the vm.awesomeMessage above.
});
});
})();
我的应用程序使用 Angular 1.2.28 和 Jasmine 2.1.3(带有 Grunt 和 Karma)。
更新:已解决!
it("Should return an awesome message", function () {
// Solved with callback parameter
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
return {
then: function(callback) {
callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
}
}
});
不,我不会这样做。
首先,无需创建模拟服务:您可以注入真实服务并对其进行监视。
其次,Angular 拥有创建承诺和解决承诺所需的一切。无需使用假 then()
函数创建假对象。
我会这样做:
describe("Awesome Controller Tests", function() {
beforeEach(module('myApp.myModule'));
var vm, awesomeService, $q, $rootScope;
beforeEach(inject(function($controller, _awesomeService_, _$q_, _$rootScope_) {
$q = _$q_;
awesomeService = _awesomeService_;
$rootScope = _$rootScope_;
vm = $controller('Awesome');
}));
it("Should return an awesome message", function () {
spyOn(awesomeService, "awesomeThingToDo").and.returnValue(
$q.when({
awesomeMessage: 'awesome message'
}));
vm.awesomeThingToDo();
// at this time, the then() callback hasn't been called yet:
// it's called at the next digest loop, that we will trigger
$rootScope.$apply();
// now the then() callback should have been called and initialized
// the message in the controller with the message of the promise
// returned by the service
expect(vm.awesomeMessage).toBe('awesome message');
});
});
无关说明:1.2.28 已经很老了。您应该迁移到最新版本。
我用可能的(错误的)解决方案更新了问题:
it("Should return an awesome message", function () {
// Solved with callback parameter
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
return {
then: function(callback) {
callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
}
}
});
我使用回调来传递模拟参数并调用真正的实现。 :D
在找了一个好的一周后answer/sample,我决定post我的问题。
我需要知道如何编写和测试这样的代码的最佳方式:
控制器
// my.controller.js
(function () {
'use strict';
angular.module('myApp.myModule').controller('Awesome', Awesome);
function Awesome($http, $state, AwesomeService) {
var vm = this; // using 'controllerAs' style
vm.init = init;
vm.awesomeThingToDo = awesomeThingToDo;
vm.init();
function awesomeThingToDo() {
AwesomeService.awesomeThingToDo().then(function (data) {
vm.awesomeMessage = data.awesomeMessage;
});
}
function init() {
vm.awesomeThingToDo(); // Should be ready on page start
}
}
})();
服务
// my.service.js
(function () {
'use strict';
angular.module('myApp.myModule').factory('AwesomeService', AwesomeService);
function AwesomeService($resource, $http) {
var service = {
awesomeThingToDo: awesomeThingToDo
}
return service;
function awesomeThingToDo() {
var promise = $http.get("/my-backend/api/awesome").then(function (response) {
return response.data;
});
return promise;
}
}
})();
我的应用程序适用于此结构。我的服务单元测试也很好。 但是我不知道如何在Controller上做单元测试。
我试过这样的事情:
规格
// my.controller.spec.js
(function () {
'use strict';
describe("Awesome Controller Tests", function() {
beforeEach(module('myApp.myModule'));
var vm, awesomeServiceMock;
beforeEach(function () {
awesomeServiceMock = { Is this a good (or the best) way to mock the service?
awesomeThingToDo: function() {
return {
then: function() {}
}
}
};
});
beforeEach(inject(function ($controller) {
vm = $controller('Awesome', {AwesomeService : awesomeServiceMock});
}));
it("Should return an awesome message", function () {
// I don't know another way do to it... :(
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function() {
return {
then: function() {
vm.awesomeMessage = 'It is awesome!'; // <-- I think I shouldn't do this.
}
}
});
vm.awesomeThingToDo(); // Call to real controller method which should call the mock service method.
expect(vm.awesomeMessage).toEqual('It is awesome!'); // It works. But ONLY because I wrote the vm.awesomeMessage above.
});
});
})();
我的应用程序使用 Angular 1.2.28 和 Jasmine 2.1.3(带有 Grunt 和 Karma)。
更新:已解决!
it("Should return an awesome message", function () {
// Solved with callback parameter
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
return {
then: function(callback) {
callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
}
}
});
不,我不会这样做。
首先,无需创建模拟服务:您可以注入真实服务并对其进行监视。
其次,Angular 拥有创建承诺和解决承诺所需的一切。无需使用假 then()
函数创建假对象。
我会这样做:
describe("Awesome Controller Tests", function() {
beforeEach(module('myApp.myModule'));
var vm, awesomeService, $q, $rootScope;
beforeEach(inject(function($controller, _awesomeService_, _$q_, _$rootScope_) {
$q = _$q_;
awesomeService = _awesomeService_;
$rootScope = _$rootScope_;
vm = $controller('Awesome');
}));
it("Should return an awesome message", function () {
spyOn(awesomeService, "awesomeThingToDo").and.returnValue(
$q.when({
awesomeMessage: 'awesome message'
}));
vm.awesomeThingToDo();
// at this time, the then() callback hasn't been called yet:
// it's called at the next digest loop, that we will trigger
$rootScope.$apply();
// now the then() callback should have been called and initialized
// the message in the controller with the message of the promise
// returned by the service
expect(vm.awesomeMessage).toBe('awesome message');
});
});
无关说明:1.2.28 已经很老了。您应该迁移到最新版本。
我用可能的(错误的)解决方案更新了问题:
it("Should return an awesome message", function () {
// Solved with callback parameter
spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
return {
then: function(callback) {
callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
}
}
});
我使用回调来传递模拟参数并调用真正的实现。 :D