在Angular中,所有内容似乎都具有陡峭的学习曲线,并且对Angular应用程序进行单元测试绝对不能逃脱这种范例。
当我开始使用TDD和Angular时,我觉得我花了两倍(可能更多)的时间来弄清楚如何测试,甚至花更多的时间来正确地设置测试。但是正如Ben Nadel在他的博客中所说的那样,角度学习过程存在起伏。他的图表绝对是我在Angular的经历。
但是,随着我在学习Angular和单元测试方面取得的进步,现在我觉得我花在建立测试上的时间要少得多,而花在使测试从红色变为绿色上的时间要多得多,这是一种很好的感觉。
因此,我遇到了多种设置单元测试以模拟服务和承诺的方法,我想我将分享我所学到的知识,并提出以下问题:
还有其他或更好的方法可以做到这一点吗?
因此,在代码上,无论如何我们都来这里-不要听一些家伙谈论他的爱,学习框架的错误成就。
这就是我开始模拟服务和承诺的方式,将使用控制器,但是显然可以在其他地方模拟服务和承诺。
describe('Controller: Products', function () { var//iable declarations $scope, $rootScope, ProductsMock = { getProducts: function () { } // There might be other methods as well but I'll stick to one for the sake of consiseness }, PRODUCTS = [{},{},{}] ; beforeEach(function () { module('App.Controllers.Products'); }); beforeEach(inject(function ($controller, _$rootScope_) { //Set up our mocked promise var promise = { then: jasmine.createSpy() }; //Set up our scope $rootScope = _$rootScope_; $scope = $rootScope.$new(); //Set up our spies spyOn(ProductsMock, 'getProducts').andReturn(promise); //Initialize the controller $controller('ProductsController', { $scope: $scope, Products: ProductsMock }); //Resolve the promise promise.then.mostRecentCall.args[0](PRODUCTS); })); describe('Some Functionality', function () { it('should do some stuff', function () { expect('Stuff to happen'); }); }); });
对于我们来说,这是可行的,但是随着时间的流逝,我认为必须有更好的方法。对于一个我讨厌
promise.then.mostRecentCall
事情 ,如果我们想重新初始化控制器,则必须将其从beforeEach块中拉出,并将其分别注入每个测试中。
一定有更好的方法…
现在我问是否有人有其他方法可以进行测试,或者对我选择的方式有想法或感觉?
然后,我遇到了另一个帖子,博客,stackoverflow示例(您发现它可能在那儿),并且看到了$ q库的用法。h!当我们只使用Angular给我们的工具时,为什么要设置一个完整的模拟承诺。我们的代码看起来更好,而且看起来更有意义- 没有难看的promise.then.mostRecent 东西 。
单元测试迭代中的下一个是:
describe('Controller: Products', function () { var//iable declarations $scope, $rootScope, $q, $controller, productService, PROMISE = { resolve: true, reject: false }, PRODUCTS = [{},{},{}] //constant for the products that are returned by the service ; beforeEach(function () { module('App.Controllers.Products'); module('App.Services.Products'); }); beforeEach(inject(function (_$controller_, _$rootScope_, _$q_, _products_) { $rootScope = _$rootScope_; $q = _$q_; $controller = _$controller_; productService = _products_; $scope = $rootScope.$new(); })); function setupController(product, resolve) { //Need a function so we can setup different instances of the controller var getProducts = $q.defer(); //Set up our spies spyOn(products, 'getProducts').andReturn(getProducts.promise); //Initialise the controller $controller('ProductsController', { $scope: $scope, products: productService }); // Use $scope.$apply() to get the promise to resolve on nextTick(). // Angular only resolves promises following a digest cycle, // so we manually fire one off to get the promise to resolve. if(resolve) { $scope.$apply(function() { getProducts.resolve(); }); } else { $scope.$apply(function() { getProducts.reject(); }); } } describe('Resolving and Rejecting the Promise', function () { it('should return the first PRODUCT when the promise is resolved', function () { setupController(PRODUCTS[0], PROMISE.resolve); // Set up our controller to return the first product and resolve the promise. expect('to return the first PRODUCT when the promise is resolved'); }); it('should return nothing when the promise is rejected', function () { setupController(PRODUCTS[0], PROMISE.reject); // Set up our controller to return first product, but not to resolve the promise. expect('to return nothing when the promise is rejected'); }); }); });
这开始像应该设置它的方式。我们可以嘲笑我们需要嘲笑的东西,可以设定解决和拒绝的诺言,这样我们就可以真正测试两种可能的结果。感觉很好…