How to test Angularjs route's resolve value on component?

China☆狼群 提交于 2019-12-23 05:10:40

问题


I'm using angular@1.5.8 and resolve some resource at the route level for one of my component. The component works as expected but my test fails now.

Error

PhantomJS 2.1.1 (Linux 0.0.0) module ag.expense-claim ExpenseClaimController: should load data FAILED
        TypeError: undefined is not an object (evaluating 'vm.attachedReceipts.results') (line 16)
        ExpenseClaimViewController@app/expenses/expense_claim.js:16:50
        ExpenseClaimViewController@[native code]
        instantiate@node_modules/angular/angular.js:4733:61
        $controller@node_modules/angular/angular.js:10369:39
        node_modules/angular-mocks/angular-mocks.js:2221:21
        $componentController@node_modules/angular-mocks/angular-mocks.js:2264:25
        test/expenseSpec.js:18:40
        invoke@node_modules/angular/angular.js:4718:24
        workFn@node_modules/angular-mocks/angular-mocks.js:3085:26
        loaded@http://localhost:9876/context.js:151:17
        inject@node_modules/angular-mocks/angular-mocks.js:3051:28
        test/expenseSpec.js:14:26
        test/expenseSpec.js:11:13
        global code@test/expenseSpec.js:1:9
        Error: No pending request to flush ! in node_modules/angular-mocks/angular-mocks.js (line 1799)
        flush@node_modules/angular-mocks/angular-mocks.js:1799:76
        test/expenseSpec.js:53:31
        loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Linux 0.0.0): Executed 43 of 43 (1 FAILED) (0.257 secs / 0.387 secs)
error Command failed with exit code 1.

Test

describe('ExpenseClaimController:', function () {
    var $scope, ctrl, attachedReceipts;

    beforeEach(inject(function ($rootScope, $componentController, $stateParams) {
        var attachedReceipts = {results: [{}]};
        $scope = $rootScope.$new();
        $stateParams.expenseClaimId = 1;
        ctrl = $componentController('expenseClaim', {
            $scope: $scope,
            attachedReceipts: attachedReceipts
        });
    }));

    it('should load data', function () {
       …
    });

Component

angular.module('ag.expenses')
    .component('expenseClaim', {
        templateUrl: '…',
        controller: ExpenseClaimViewController,
        controllerAs: 'vm',
        bindings: {
            attachedReceipts: "<"
        }
    });

function ExpenseClaimViewController($stateParams, $uibModal, API, gettextCatalog, alert) {
    var vm = this;
    vm.attachedReceipts = vm.attachedReceipts.results;
    …
}

Route

 .state('expense-claim', {
        url: '/home_expense_report/:expenseClaimId',
        template: '<expense-claim attached-receipts="$resolve.attachedReceipts"></expense-claim>',
        resolve: {
            attachedReceipts: function (API, $stateParams) {
                return API.TransportCostAttachment.query({expenseClaimId: $stateParams.expenseClaimId}).$promise;
            }
        }
    })

Question

I implement my solution based on How can I mock ui-router's resolve values when testing a state's configuration? but still can't get it to works. What am I missing?


回答1:


the resolve in angular 1.5 is not DI not like angular 2, so for 1.5 you should pass it as a binding, something like this:

var bindings = { attachedReceipts: attachedReceipts };
ctrl = $componentController('expenseClaim', {
            $scope: $scope
        }, bindings);



回答2:


In addition to Ameer response, I'd like to provide a few more details as I struggled a lot to find them, if you have a component and you need to unit test the controller directly or if you want to test the template, you have two ways of creating the controller that have bindings (whether they are coming from a resolve from ui-router or not doesn't matter)

- Unit testing the component controller:

Like Ameer said, you should pass it as a binding:

beforeEach(inject(function(_$componentController_) {
  $componentController = _$componentController_;
}));

it('should expose a `hero` object', function() {
  // Here we are passing actual bindings to the component
  var bindings = {hero: {name: 'Wolverine'}};
  var ctrl = $componentController('heroDetail', null, bindings);

  expect(ctrl.hero).toBeDefined();
  expect(ctrl.hero.name).toBe('Wolverine');
});

$onInit method:

If your controller has $onInit() method, it won't be executed automatically like you expect, you need to manually call the method after creating the controller, more details on this issue here

- Unit testing the template of the component:

When unit testing the template of a component, you still need to instantiate the controller, this is how you can do:

beforeEach(inject(($injector) => {
  $rootScope = $injector.get('$rootScope');
  $compile = $injector.get('$compile');
  scope = $rootScope.$new();
  scope.attachedReceipts = {results: [{}]};;

  template = $compile('<expense-claim attached-receipts="attachedReceipts"></expense-claim>')(scope);
  scope.$apply();
});

it('should test something', () => {
  //Example of something you could test
  expect(template.find('h3').html()).to.eq('this is our first content page');
});

With this second method, the $onInit() method will be called, so you don't need to call it manually. A full tutorial on how to unit test a template within a component can be found here



来源:https://stackoverflow.com/questions/40717165/how-to-test-angularjs-routes-resolve-value-on-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!