我正在尝试测试在控制器中执行的服务http调用的响应:
控制器:
define(['module'], function (module) {
'use strict';
var MyController = function ($scope, MyService) {
var vm = this;
$scope.testScope = 'karma is working!';
MyService.getData().then(function (data) {
$scope.result = data.hour
vm.banner = {
'greeting': data.greeting
}
});
};
module.exports = ['$scope', 'MyService', MyController ];
});
单元测试:
define(['require', 'angular-mocks'], function (require) {
'use strict';
var angular = require('angular');
describe("<- MyController Spec ->", function () {
var controller, scope, myService, serviceResponse;
serviceResponse= {
greeting: 'hello',
hour: '12'
};
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$controller_, _$rootScope_, _MyService_, $q) {
scope = _$rootScope_.$new();
var deferred = $q.defer();
deferred.resolve(serviceResponse);
myService = _MyService_;
spyOn(myService, 'getData').and.returnValue(deferred.promise);
controller = _$controller_('MyController', {$scope: scope});
scope.$apply();
}));
it('should verify that the controller exists ', function() {
expect(controller).toBeDefined();
});
it('should have testScope scope equaling *karma is working*', function() {
expect(scope.testScope ).toEqual('karma is working!');
});
});
});
如何测试http
请求的执行情况,并返回与serviceResponse
和$scope.result
绑定的vm.banner greeting
我试过了:
define(['require', 'angular-mocks'], function (require) {
'use strict';
var angular = require('angular');
describe("<- MyController Spec ->", function () {
var controller, scope, myService, serviceResponse, $httpBackend;
serviceResponse= {
greeting: 'hello',
hour: '12'
};
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$controller_, _$rootScope_, _MyService_, _$httpBackend_) {
scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_
$httpBackend.expectGET("/my/endpoint/here").respond(serviceResponse);
myService = _MyService_;
spyOn(myService, 'getData').and.callThrough();
controller = _$controller_('MyController', {$scope: scope});
scope.$apply();
}));
it('should call my service and populate scope.result ', function() {
myService.getData();
expect(scope.result ).toEqual(serviceResponse.hour);
});
it('should verify that the controller exists ', function() {
expect(controller).toBeDefined();
});
it('should have testScope scope equaling *karma is working*', function() {
expect(scope.testScope ).toEqual('karma is working!');
});
});
});
错误:
[should call my service and populate scope.result ----- Expected undefined to be defined.
答案 0 :(得分:3)
我认为问题在于您的服务会返回一个承诺,所以当您执行此操作时it
it('should call my service and populate scope.result ', function() {
myService.getData();
expect(scope.result ).toEqual(serviceResponse.hour);
});
您的服务在到达expect
之前可能尚未得到解决,因此您必须先等待承诺的then
并在内部进行预期。
你可以做的是分配给$ scope.result的承诺
var MyController = function ($scope, MyService) {
var vm = this;
$scope.testScope = 'karma is working!';
$scope.result = MyService.getData().then(function (data) {
$scope.result = data.hour
vm.banner = {
'greeting': data.greeting
}
});
};
然后在你的测试中,你可以做类似
的事情it('should call my service and populate scope.result ', function() {
//myService.getData(); <- you don't have to call this
scope.result.then(function(){
expect(scope.result).toEqual(serviceResponse.hour);
});
});
您需要模拟$httpBackend
并期望某些请求并提供模拟响应数据。这里是角docs
beforeEach(inject(function($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// backend definition common for all tests
$httpBackend.when('GET', '/auth.py')
.respond({userId: 'userX'}, {'A-Token': 'xxx'});
}));
现在,每当$ http调用get /auth.py
时,它都会回复模拟数据{userId: 'userX'}, {'A-Token': 'xxx'}
答案 1 :(得分:2)
您不应该在控制器中测试服务的行为。在单独的测试中测试控制器和服务。
控制器测试
在您的控制器中,您不应该关心来自服务的数据来自何处,您应该测试返回的结果是否按预期使用。验证控制器是否在调用时调用所需的方法,并确保$scope.result
和vm.banner
的值符合您的预期。
类似的东西:
it('should have called my service and populated scope.result ', function() {
expect(myService.getData).toHaveBeenCalled();
expect(scope.result).toEqual(serviceResponse.hour);
expect(vm.banner).toEqual({greeting:serviceResponse.greeting});
});
服务测试
另一方面,您的服务测试应该知道$http
调用并且应该验证响应,因此使用来自@Luie Almeda响应的相同资源,为您的服务编写单独的测试,调用该方法getData()
执行模拟的$ http调用并返回所需的结果。
类似的东西:
it('Should return serviceResponse', function () {
var serviceResponse= {
greeting: 'hello',
hour: '12'
};
var myData,
$httpBackend.whenGET('GET_DATA_URL').respond(200, userviceResponse);
MyService.getData().then(function (response){
myData = response
})
$httpBackend.flush();
expect(myData).toBe(serviceResponse);
});
您需要使用GET_DATA_URL
调用的正确网址替换MyService.getData()
。始终尝试将测试保存在与代码相同的模块中。在服务测试中测试控制器测试中的控制器代码和服务代码。
答案 2 :(得分:2)
我理解的是,在期待结果时,你已经制作了一个Ajax&amp;在承诺得到解决时设置数据值。
基本上,当您在控制器实例化时调用myService.getData()
方法时,它会执行$http
,它会间接执行$httpBackend
ajax并从中返回mockData
。但是一旦你拨打电话,你就不会等待电话那个完成电话。然后你打电话给你的assert
声明,这会使你的统计失败。
请查看以下代码注释以获取解释
it('should call my service and populate scope.result ', function() {
//on controller instantiation only we make $http call
//which will make your current execution state of your code in async state
//and javascript transfer its execution control over next line which are assert statement
//then obiviously you will have undefined value.
//you don't need to call below line here, as below method already called when controller instantiated
//myService.getData();
//below line gets called without waiting for dependent ajax to complete.
expect(scope.result ).toEqual(serviceResponse.hour);
});
所以现在你明白你需要告诉我们的测试代码,我们需要等待ajax调用结束时执行assert语句。因此,您可以使用$httpBackend.flush()
方法在这种情况下对您有所帮助。在将控制传递给下一行之前,它将清除所有$httpBackend
个队列清除队列。
it('should call my service and populate scope.result ', function() {
$httpBackend.flush();// will make sure above ajax code has received response
expect(scope.result ).toEqual(serviceResponse.hour);
});