【问题标题】:Manage angularjs controller init with karma tests使用业力测试管理 angularjs 控制器初始化
【发布时间】:2016-04-29 18:22:48
【问题描述】:

我见过这样的问题,他们都说“将逻辑提取到服务中并模拟服务”。很简单,除了我有尽可能多的。

因此,当控制器初始化时,我调用了$scope.getWidgets(),此方法调用widgetService 为用户获取小部件列表,然后它将显示使用notifyService 的toast 通知。如果widgetService 拒绝其承诺或用户没有小部件,则调用notifyService。这一切都是在控制器初始化时完成的。

$scope.getWidgets = function () {
    widgetService.getWidgets()
        .then(function (widgets) {
            if (widgets.length === 0) {
                notifyService.notify('no widgets');
            }

            $scope.widgets = widgets;
        })
        .catch(function (e) {
            notifyService.notify('oh noes');
        });
}

//called at bottom of controller
$scope.getWidgets();

现在我所有的测试都不需要运行任何摘要周期,运行运行承诺等。我试图测试的方法调用服务来保存小部件。如果成功或失败,它将再次调用notifyService 向用户发送通知。我正在测试通知是使用业力的spyOn 触发的,并且还测试了一些标志设置是否正确。没有真正的逻辑,这是在服务中完成的。

$scope.saveWidget = function (widget) {
    widgetService.saveWidget(widget)
        .then(function () {
            notifyService.notify('all dandy');
            //set some scope flags, no logic
        })
        .catch(function (e) {
            notifyService.notify('oh noes');
            //set some more scope flags, no logic
        });
}

所以现在我必须运行摘要循环来触发我的承诺。 The Trouble is my mock for widgetService.getWidgets() 返回一个承诺。因此,当我首先运行摘要时,它会解析在我的 init 中注册的承诺,它会调用通知服务,该服务会激活我的间谍,以虚假数据结束我的测试。

这是我的测试,(请原谅大代码块,我试图将它尽可能地精简)。

describe('tests', function () {
    var scope, controlelr, _mockWidgetService, _mockNotifyService;

    beforeEach(function () {
        //Init the service mocks, angular.noop for methods etc
        _mockWidgetService = init();
        _mockNotifyService = init();
    });

    beforeEach(function () {
        //Regisetr hooks to pull in my mocked services
        angular.mock.module('myModule');

        angular.mock.module(function($provide) {
            $provide.value('widgetService', _mockWidgetService);
            $provide.value('notifyService', _mockNotifyService);
        })
    });

    angular.mock.inject(function ($controller, $rootScope, widgetService, notifyService) {
        //create scope and inject services to create controller
        scope = $rootScope.$new();

        //Trouble is $scope.getWidgets() is called now.
        controller = $controller('testController', {
            $scope: scope,
            widgetService: widgetService,
            notifyService: notifyService
        });
    });

    describe('getWidgets', function() {
        it('myTest', function () {
            //I cannow manipulate mock services moreso if needed just for this test
            spyOn(_mockNotifyService, 'notfy');

            //Call save widget
            scope.saveWidget();

            scope.$digest();

            expect(_mockNotifyService.notify).toHaveBeenCalledWith(//values);
        });
    });
});

所以你可以看到我在苦苦挣扎,我可以模拟初始化代码所需的所有模拟,但我只模拟该测试所需的服务。另外,我不喜欢运行每个测试的初始化代码可能会导致错误错误的想法(它有自己的测试)。

对于此类用例的任何建议或解决方案将不胜感激。

【问题讨论】:

  • 这个单元测试在 IMO 完全没有意义。你需要对你的服务进行单元测试,但是在你的控制器中没有逻辑,所以没有什么要单元测试的。
  • @webduvet 好吧,也许这个例子不是最好的。我的控制器确实有一些小逻辑,例如根据标志调用不同的服务,向用户显示不同的 toast 通知等。我相信这些小逻辑在测试中具有价值。正如我所说,我的服务已经过全面测试。
  • 这样的行为应该包含在 e2e 测试而不是单元测试中。您的用户肯定不会在 chrome 控制台中设置标志。如果我严格要求,我会说控制器根本不需要逻辑。
  • @webduvet 我下次会记住这一点。感谢您的意见。编辑:你能解释一下为什么你的用户不会改变服务中的承诺链。
  • @webduvet 抱歉,我的意思不是要听起来防御,只是想理解 :) 我认为可能存在混淆,我不是在尝试测试一系列模拟服务,我理解这些问题那里。但此外测试,当服务响应数据时,控制器正确处理该数据,或者通过调用我 spyOn 断言它被调用的另一个服务或设置标志等。它不是嵌套的混乱或十亿 if else's 但它也不是单if(isTrue){} 要么。感谢您的意见:)

标签: javascript angularjs unit-testing karma-jasmine


【解决方案1】:

我找到了一个似乎可行的潜在解决方案。我不完全确定它的最佳做法,所以如果有更好的选择,我非常感谢听到它们。

基本上不是在每次测试之前用一个已解决的承诺来模拟我的widgetService.getWidgets,而是用一个永远不会解决的延迟承诺来模拟它。对于不需要它的测试的想法,init 代码将永远不会运行,因为 promise 永远不会解决。

确实需要它运行的测试只需覆盖模拟并让widgetService.getWidgets 返回所需的内容。

这确实意味着一些小的变化,必须为每个测试手动实例化控制器(在可用的情况下使用 beforeEach),而不是在第一个描述的顶部)。

【讨论】:

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