【问题标题】:how to check a function in an injected controller?如何检查注入控制器中的功能?
【发布时间】:2016-08-28 15:23:33
【问题描述】:

我目前只是尝试测试我的控制器上的 getTodaysHours 函数是否已被调用。最终,该函数应该从模拟 JSON data 中获取 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 常量模拟 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();
    });
});

然后我尝试注入控制器(不确定语法是否正确):

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) 供应商控制器应该通过 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) 供应商控制器应调用 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 (evalating 'vm.getTodaysHours') in /Users/adminuser/Documents/workspace/thcmaps-ui/.tmp/serve/app/index.module.js(第9行) /Users/adminuser/Documents/workspace/thcmaps-ui/.tmp/serve/app/index.module.js:9:244419

【问题讨论】:

  • 如果你正确地模拟了控制器,那么你应该得到第二个错误。但是,我不确定为什么您会遇到第一个错误。您确定控制器定义中没有错误吗?如果你能在 Plunker 中重现这个错误,我可以帮你解决,否则,你就得靠你自己了。

标签: javascript angularjs json unit-testing jasmine


【解决方案1】:

在使用$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'];
  }
}

我使用 ES6 编写了这个控制器,请注意,依赖关系是在类声明脚下的静态 $injectgetter 中定义的。这将在实例化时被 AngularJS 拾取。

如您所见,控制器依赖于$rootScope$log 提供程序。当为了测试目的模拟这个控制器时,你必须像这样注入控制器依赖项:

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
  });
});

最新版本的 Jasmine 使开发人员能够在整个测试过程中利用 this 关键字。

任何beforeEachafterEachit 声明都将共享对this 的相同引用,允许您避免创建封闭变量(如var controller,如上所示)并避免创建不必要的全局变量。例如:

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 调用中的第二个参数,它必须是一个对象,其中包含您的控制器(在本例中为“MyController”)所依赖的预期依赖项。

这背后的原因只是为了让开发人员可以将模拟服务、工厂、提供者等传递给控制器​​,以替代间谍。


对于 Jasmine 文档关于 this 与测试的使用的无用链接表示歉意,由于锚标签的设置方式(锚标签),我无法将直接链接添加到页面的正确部分包含一个<code></code> 块,doh!)。

【讨论】:

  • 感谢 Iain,我注入了依赖项,但仍然得到相同的错误,我的 beforeEach 现在看起来像这样:jsfiddle.net/3bj4tg9d
  • 能否请您添加您遇到的问题,上面的代码应该没有任何问题。您的来源很可能存在问题,而不是您的测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-21
  • 2014-09-09
  • 2019-01-21
  • 2014-10-14
相关资源
最近更新 更多