javascript - 如何检查注入(inject) Controller 中的功能?

标签 javascript angularjs json unit-testing jasmine

我目前只是想测试我的 Controller 上的 getTodaysHours 函数是否被调用。最终,该函数应该从模拟 JSON 数据 中获取 hours 并在参数匹配时传递,但我停留在第一部分。

vendor.controller

export class VendorController {
    constructor($rootScope, data, event, toastr, moment, _, distanceService, vendorDataService, userDataService, stateManagerService) {
        'ngInject';
        //deps
        this.$rootScope = $rootScope;
        this.toastr = toastr;
        this._ = _;
        this.userDataService = userDataService;
        this.vendorDataService = vendorDataService;
        this.stateManagerService = stateManagerService;
        this.event = event;

        //bootstrap
        data.isDeepLink = true;
        this.data = data;
        this.data.last_update = moment(this.data.updated_at).format('MM/DD/YY h:mm A');
        this.data.distance = distanceService.getDistance(this.data.loc.lng, this.data.loc.lat);
        this.data.todaysHours = this.getTodaysHours();
        this.data.rating_num = Math.floor(data.rating);


        this.hasReviewed = (userDataService.user.reviewed[data._id]) ? true : false;
        this.isGrid = false;
        this.isSearching = false;
        this.hideIntro = true;
        this.menuCollapsed = true;
        this.filterMenuCollapsed = true;

        this.selectedCategory = 'All';
        this.todaysHours = '';
        this.type = '';
        this.searchString = '';

        this.reviewScore = 0;

        this.today = new Date().getDay();

        this.vendorDataService.currentVendor = data;

        //load marker onto map
        $rootScope.$broadcast(event.ui.vendor.pageLoad, data);

        //get menu
        vendorDataService.getVendorMenu(data._id)
            .then((res)=> {
                this.data.menu = res.menu;
                this.menuContainer = this.data.menu;
                this.totalResults = this.getTotalResults();
                this.availableMenuCategories = this.getAvailableMenuCategories();
            })
            .catch(() => {
                this.toastr.error('Whoops, Something went wrong! We were not able to load the menu.',  'Error');
            });
    }

    //get todays hours
    getTodaysHours() {
        let today = this.data.hours[new Date().getDay()];
        return (today.opening_time || '9:00am') + ' - ' + (today.closing_time || '5:00pm');
    }  
}

当我使用$provide constant 模拟 JSON 数据时,第一个测试通过了

describe('vendor controller', () => {
    let vm,
        data = {"_id":"56b54f9368e685ca04aa0b87","lat_lon":"33.713018,-117.841101","hours":[{"day_of_the_week":"sun","closing_time":" 7:00pm","opening_time":"11:00am","day_order":0,"id":48880},...];

    beforeEach(angular.mock.module('thcmaps-ui', ($provide) => {
        $provide.constant('data', new data);
      }));

    //first test
    it('should pass', () => {
        expect(data._id).toEqual('56b54f9368e685ca04aa0b87');
    });

    //second test
    it('should call getTodaysHours', () => {
    expect(vm.getTodaysHours()).toHaveBeenCalled();
    });
});

然后我尝试注入(inject) Controller (不确定语法是否正确):

beforeEach(angular.mock.module('thcmaps-ui', ($provide) => {
    $provide.constant('data', new data);
  }));
beforeEach(inject(($controller) => {
    vm = $controller('VendorController');
    spyOn(vm,'getTodaysHours').and.callThrough();
}));

它给了我某种 forEach 错误。第二个测试在评估 vm.getTodaysHours() 时给我一个未定义的错误:

PhantomJS 2.1.1 (Mac OS X 0.0.0) vendor controller should pass FAILED forEach@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:341:24 loadModules@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:4456:12 createInjector@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:4381:22 workFn@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular-mocks/angular-mocks.js:2507:60 /Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:4496:53

PhantomJS 2.1.1 (Mac OS X 0.0.0) vendor controller should call getTodaysHours FAILED forEach@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:341:24 loadModules@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:4456:12 createInjector@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:4381:22 workFn@/Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular-mocks/angular-mocks.js:2507:60 /Users/adminuser/Documents/workspace/thcmaps-ui/bower_components/angular/angular.js:4496:53 TypeError: undefined is not an object (evaluating 'vm.getTodaysHours') in /Users/adminuser/Documents/workspace/thcmaps-ui/.tmp/serve/app/index.module.js (line 9) /Users/adminuser/Documents/workspace/thcmaps-ui/.tmp/serve/app/index.module.js:9:244419

最佳答案

在使用 $controller 实例化它时,您需要注入(inject) Controller 的依赖项.例如,考虑以下 Controller :

class MyController {
  constructor($rootScope, $log) {
    // Store the controllers dependencies
    this.$rootScope = $rootScope;
    this.$log = $log;
  }

  // Return obituary value from the $rootScope
  getValue() {
    this.$log.debug('Retrieving value');
    return this.$rootScope.foobar;
  }

  // Get the current date
  getDate() {
    this.$log.debug('Getting date');
    return Date.now()
  }

  static get $inject() {
    return ['$scope', '$log'];
  }
}

I've written this controller using ES6, note that the dependencies are defined within the static $injectgetter at the foot of the class declaration. This will be picked up by AngularJS upon instantiation.

如您所见, Controller 依赖于 $rootScope$log提供商。当出于测试目的模拟此 Controller 时,您必须像这样注入(inject) Controller 依赖项:

describe('Spec: MyController', () => {
  var controller;

  beforeEach(inject(($rootScope, $log, $controller) => {
    controller = $controller('MyController', {
      $rootScope,
      $log
    });
  });

  it('should return a value from the $rootScope', () => {
    var value = controller.getValue();
    // ... perform checks
  });

  it('should return the current date', () => {
    var date = controller.getDate();
    // ... perform checks
  });
});

More recent versions of Jasmine enable developers to leverage the this keyword throughout their tests.

Any beforeEach, afterEach, and it declarations will all share the same reference to this, allowing you to avoid creating enclosed variables (like var controller, as seen above) and also avoid creating unnecessary globals. For example:

beforeEach(inject(function ($rootScope, $log, $controller) {
  this.controller = $controller('MyController', {
    $rootScope,
    $log
  });
});

it('should return a value from the $rootScope', function () {
  this.value = controller.getValue();
  // ... perform checks
});

注意 $controller 调用中的第二个参数,这必须是一个对象,其中包含您的 Controller (在本例中为“MyController”)所依赖的预期依赖项。

这背后的原因很简单,就是允许开发人员将模拟服务、工厂、提供者等传递给 Controller ​​,作为 spy 的替代方案。


Apologies for the unuseful link to the Jasmine documentation regarding the usage of this with tests, I couldn't add a direct link to the correct section of the page due to how their anchor tags are set out (the anchor tag contains a <code></code> block, doh!).

关于javascript - 如何检查注入(inject) Controller 中的功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37006550/

相关文章:

php - AJAX/Javascript 无法更新网页

javascript - angularjs异步http发布更新范围数据

javascript - Angular 、 promise 、ngResource

javascript - 如何将多种数据/数据类型发布到 php?

json - 如何将复杂对象映射到简单字段

javascript - 如果需要更改函数中的变量必须是公共(public)变量吗?

javascript - 括号不匹配 : a quick way to find them?

javascript - 通过 ng-change 传递函数参数不起作用

javascript - 为什么指令中的值没有得到更新 - Angular Js?

php - 将 unicode\u 序列(如\u041a)转换为 php 中的普通 utf-8 文本