gpt4 book ai didi

javascript - 使用 success() 和 error () 测试 Controller

转载 作者:行者123 更新时间:2023-12-02 23:24:24 25 4
gpt4 key购买 nike

我正在尝试找出在 Controller 中进行单元测试成功和错误回调的最佳方法。只要 Controller 仅使用默认的 $q 函数(例如“then”),我就可以模拟服务方法(请参见下面的示例)。当 Controller 响应“成功”或“错误” promise 时,我遇到了问题。 (抱歉,如果我的术语不正确)。

这是一个示例 Controller \服务

var myControllers = angular.module('myControllers');

myControllers.controller('SimpleController', ['$scope', 'myService',
function ($scope, myService) {

var id = 1;
$scope.loadData = function () {
myService.get(id).then(function (response) {
$scope.data = response.data;
});
};

$scope.loadData2 = function () {
myService.get(id).success(function (response) {
$scope.data = response.data;
}).error(function(response) {
$scope.error = 'ERROR';
});
};
}]);


cocoApp.service('myService', [
'$http', function($http) {
function get(id) {
return $http.get('/api/' + id);
}
}
]);

我有以下测试

'use strict';

describe('SimpleControllerTests', function () {

var scope;
var controller;
var getResponse = { data: 'this is a mocked response' };

beforeEach(angular.mock.module('myApp'));

beforeEach(angular.mock.inject(function($q, $controller, $rootScope, $routeParams){

scope = $rootScope;
var myServiceMock = {
get: function() {}
};

// setup a promise for the get
var getDeferred = $q.defer();
getDeferred.resolve(getResponse);
spyOn(myServiceMock, 'get').andReturn(getDeferred.promise);

controller = $controller('SimpleController', { $scope: scope, myService: myServiceMock });
}));


it('this tests works', function() {
scope.loadData();
expect(scope.data).toEqual(getResponse.data);
});

it('this doesnt work', function () {
scope.loadData2();
expect(scope.data).toEqual(getResponse.data);
});
});

第一个测试通过,第二个测试失败,并出现错误“TypeError:对象不支持属性或方法‘成功’”。我在这个例子中得到了 getDeferred.promise没有成功功能。好的,问题是,编写此测试的好方法是什么,以便我可以测试模拟服务的“成功”、“错误”和“然后”条件?

我开始认为我应该避免在 Controller 中使用 success() 和 error()...

编辑

因此,经过更多思考,并感谢下面的详细答案,我得出的结论是,在 Controller 中处理成功和错误回调是不好的。 正如 HackedByChinese 提到的success\error 下面是 $http 添加的语法糖。因此,实际上,通过尝试处理成功\错误,我让 $http 问题泄漏到我的 Controller 中,这正是我试图通过将 $http 调用包装在服务中来避免的情况。我要采取的方法是更改​​ Controller 不使用 success\error:

myControllers.controller('SimpleController', ['$scope', 'myService',
function ($scope, myService) {

var id = 1;
$scope.loadData = function () {
myService.get(id).then(function (response) {
$scope.data = response.data;
}, function (response) {
$scope.error = 'ERROR';
});
};
}]);

这样我就可以通过在延迟对象上调用resolve()和reject()来测试错误\成功条件:

'use strict';

describe('SimpleControllerTests', function () {

var scope;
var controller;
var getResponse = { data: 'this is a mocked response' };
var getDeferred;
var myServiceMock;

//mock Application to allow us to inject our own dependencies
beforeEach(angular.mock.module('myApp'));
//mock the controller for the same reason and include $rootScope and $controller
beforeEach(angular.mock.inject(function($q, $controller, $rootScope, $routeParams) {

scope = $rootScope;
myServiceMock = {
get: function() {}
};
// setup a promise for the get
getDeferred = $q.defer();
spyOn(myServiceMock, 'get').andReturn(getDeferred.promise);
controller = $controller('SimpleController', { $scope: scope, myService: myServiceMock });
}));

it('should set some data on the scope when successful', function () {
getDeferred.resolve(getResponse);
scope.loadData();
scope.$apply();
expect(myServiceMock.get).toHaveBeenCalled();
expect(scope.data).toEqual(getResponse.data);
});

it('should do something else when unsuccessful', function () {
getDeferred.reject(getResponse);
scope.loadData();
scope.$apply();
expect(myServiceMock.get).toHaveBeenCalled();
expect(scope.error).toEqual('ERROR');
});
});

最佳答案

正如有人在删除的答案中提到的,成功错误是由$http添加的语法糖,所以当它们不存在时你创造你自己的 promise 。您有两个选择:

1 - 不要模拟服务并使用 $httpBackend 设置期望并刷新

这个想法是让您的 myService 像平常一样行事,而不知道它正在被测试。 $httpBackend 将允许您设置期望和响应,并刷新它们,以便您可以同步完成测试。 $http 不会变得更明智,它返回的 promise 看起来和功能都像真实的 promise 一样。如果您进行简单的测试且对 HTTP 的期望很少,则此选项非常有用。

'use strict';

describe('SimpleControllerTests', function () {

var scope;
var expectedResponse = { name: 'this is a mocked response' };
var $httpBackend, $controller;

beforeEach(module('myApp'));

beforeEach(inject(function(_$rootScope_, _$controller_, _$httpBackend_){
// the underscores are a convention ng understands, just helps us differentiate parameters from variables
$controller = _$controller_;
$httpBackend = _$httpBackend_;
scope = _$rootScope_;
}));

// makes sure all expected requests are made by the time the test ends
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});

describe('should load data successfully', function() {

beforeEach(function() {
$httpBackend.expectGET('/api/1').response(expectedResponse);
$controller('SimpleController', { $scope: scope });

// causes the http requests which will be issued by myService to be completed synchronously, and thus will process the fake response we defined above with the expectGET
$httpBackend.flush();
});

it('using loadData()', function() {
scope.loadData();
expect(scope.data).toEqual(expectedResponse);
});

it('using loadData2()', function () {
scope.loadData2();
expect(scope.data).toEqual(expectedResponse);
});
});

describe('should fail to load data', function() {
beforeEach(function() {
$httpBackend.expectGET('/api/1').response(500); // return 500 - Server Error
$controller('SimpleController', { $scope: scope });
$httpBackend.flush();
});

it('using loadData()', function() {
scope.loadData();
expect(scope.error).toEqual('ERROR');
});

it('using loadData2()', function () {
scope.loadData2();
expect(scope.error).toEqual('ERROR');
});
});
});

2 - 返回一个完全模拟的 promise

如果您正在测试的东西具有复杂的依赖关系,并且所有设置都令人头疼,您可能仍然想像您尝试的那样模拟服务和调用本身。不同之处在于您需要完全模拟 Promise。这样做的缺点可能是创建所有可能的模拟 promise ,但是您可以通过创建自己的函数来创建这些对象来使这变得更容易。

之所以有效,是因为我们假装它通过立即调用 successerrorthen 提供的处理程序来解决,从而导致它同步完成。

'use strict';

describe('SimpleControllerTests', function () {

var scope;
var expectedResponse = { name: 'this is a mocked response' };
var $controller, _mockMyService, _mockPromise = null;

beforeEach(module('myApp'));

beforeEach(inject(function(_$rootScope_, _$controller_){
$controller = _$controller_;
scope = _$rootScope_;

_mockMyService = {
get: function() {
return _mockPromise;
}
};
}));

describe('should load data successfully', function() {

beforeEach(function() {

_mockPromise = {
then: function(successFn) {
successFn(expectedResponse);
},
success: function(fn) {
fn(expectedResponse);
}
};

$controller('SimpleController', { $scope: scope, myService: _mockMyService });
});

it('using loadData()', function() {
scope.loadData();
expect(scope.data).toEqual(expectedResponse);
});

it('using loadData2()', function () {
scope.loadData2();
expect(scope.data).toEqual(expectedResponse);
});
});

describe('should fail to load data', function() {
beforeEach(function() {
_mockPromise = {
then: function(successFn, errorFn) {
errorFn();
},
error: function(fn) {
fn();
}
};

$controller('SimpleController', { $scope: scope, myService: _mockMyService });
});

it('using loadData()', function() {
scope.loadData();
expect(scope.error).toEqual("ERROR");
});

it('using loadData2()', function () {
scope.loadData2();
expect(scope.error).toEqual("ERROR");
});
});
});

即使在大型应用程序中,我也很少选择选项 2。

无论如何,您的 loadDataloadData2 http 处理程序有一个错误。他们引用了 response.datahandlers将直接使用解析后的响应数据调用,而不是响应对象(因此它应该是 data 而不是 response.data)。

关于javascript - 使用 success() 和 error () 测试 Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23731637/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com