【问题标题】:Testing the controller passed to an Angular Material Dialog instance测试传递给 Angular Material Dialog 实例的控制器
【发布时间】:2015-12-08 03:17:06
【问题描述】:

首先,我正在尝试对传递给Angular Material Dialog 实例的控制器进行单元测试。

作为一个普遍的问题,单独测试这样的控制器更有意义还是通过实际调用$mdDialog.show()

我正在尝试第一种方法,但遇到了一些问题,主要与 Angular Material 如何将“本地”绑定到控制器有关。

这是我用来在我的源代码中调用对话框的代码,它按预期工作:

$mdDialog.show({
    controller: 'DeviceDetailController',
    controllerAs: 'vm',
    locals: {deviceId: "123"},
    bindToController: true,
    templateUrl: 'admin/views/deviceDetail.html',
    parent: angular.element(document.body),
    targetEvent: event
});

我不相信docs 已经更新,但是从版本 0.9.0 左右开始,在调用构造函数时控制器可以使用本地变量(请参阅this issue on Github)。这是一个被测试的控制器构造函数的精简版本,所以你可以看到为什么我需要在控制器“实例化”时传入和可用的变量:

function DeviceDetailController(devicesService) {
    var vm = this;

    vm.device = {};
//    vm.deviceId = null;           //this field is injected when the dialog is created, if there is one. For some reason I can't pre-assign it to null.

    activate();

    //////////
    function activate() {
        if (vm.deviceId != null) {
            loadDevice();
        }
    }

    function loadDevice() {
        devicesService.getDeviceById(vm.deviceId)
            .then(function(data) {
                vm.device = data.collection;
            };
    }
}

我正在尝试在调用之前将 deviceId 传递给构造函数时测试设备是否已分配给 vm.device。

测试(茉莉和诗侬,由业力运行):

describe('DeviceDetailController', function() {
    var $controllerConstructor, scope, mockDevicesService;

    beforeEach(module("admin"));

    beforeEach(inject(function ($controller, $rootScope) {
        mockDevicesService = sinon.stub({
            getDeviceById: function () {}
        });
        $controllerConstructor = $controller;
        scope = $rootScope.$new();
    }));

    it('should get a device from devicesService if passed a deviceId', function() {
        var mockDeviceId = 3;
        var mockDevice = {onlyIWouldHaveThis: true};
        var mockDeviceResponse = {collection: [mockDevice]};
        var mockDevicePromise = {
            then: function (cb) {
                cb(mockDeviceResponse);
            }
        };

        var mockLocals = {deviceId: mockDeviceId, $scope: scope};

        mockDevicesService.getDeviceById.returns(mockDevicePromise);

        var ctrlConstructor = $controllerConstructor('DeviceDetailController as vm', mockLocals, true);
        angular.extend(ctrlConstructor.instance, mockLocals);
        ctrlConstructor();

        expect(scope.vm.deviceId).toBe(mockDeviceId);
        expect(scope.vm.device).toEqual(mockDevice);
    });
});

当我运行它时,第一个断言通过,第二个断言失败(“预期对象({ })等于对象({ onlyIWouldHaveThis: true })。”),这表明 deviceId 正在被注入控制器的范围,但显然没有及时让 activate() 方法中的 if 子句看到它。

你会注意到我试图模仿Angular Material 使用的基本过程,通过调用$controller() 并将第三个参数设置为'true',这导致$controller() 返回控制器构造函数,而不是到结果控制器。然后我应该能够使用我的局部变量扩展构造函数(就像 Angular Material 在上面链接的代码中所做的那样),然后调用构造函数来实例化控制器。

我尝试了很多方法,包括通过调用$rootScope.$new(true) 将隔离范围传递给控制器​​,但没有效果(我实际上不能说我完全理解隔离范围,但 $mdDialog 默认使用它)。

感谢任何帮助!

【问题讨论】:

    标签: javascript angularjs unit-testing karma-jasmine angular-material


    【解决方案1】:

    我会尝试的第一件事是从您对 $controller 的调用中丢失“as vm”。您可以将返回值用于您的期望而不是测试范围。

    试试这个:

    var ctrlConstructor = $controllerConstructor('DeviceDetailController', mockLocals, true);
        angular.extend(ctrlConstructor.instance, mockLocals);
        var vm = ctrlConstructor();
    
        expect(vm.deviceId).toBe(mockDeviceId);
        expect(vm.device).toEqual(mockDevice);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多