角度单位测试状态随着决心而改变

时间:2015-01-16 15:08:09

标签: angularjs unit-testing angular-ui-router

方案

我建立了2种状态的搜索表单: 第一个状态触发第二个(onclick),并使用$ state.go()函数设置一些状态参数。 第二个状态具有获取状态参数的解析函数,并解析搜索结果。

这种方法看起来更简洁,选择经典方法:将搜索服务注入状态1,获取结果,将其保存在搜索服务中,更改状态并从注入的服务加载结果。

问题

如何对此进行单元测试?

最初的想法是我应该进行“应该使用searchTerm触发定位并切换到结果页面”的测试。 它失败了,因为州没有用茉莉花妥善解决。 有时它会被锁定在第一状态,在其他情况下,状态总是''。

当我从配置中删除了解析块时,它按预期工作,并且测试通过,因此问题显然在解析块中。奇怪的是,我没有得到“未知提供者”的错误。

有任何想法或想法如何解决这个问题?

关于StackOverflow的最近问题是这样的: Angular ui router unit testing (states to urls) 但是用户手动调用状态更改,而在我的场景中,它由另一个函数调用。

单元测试代码:

describe('SearchController tests', function(){
    var ctrl, $state, $rootScope, $httpBackend;

    // instantiate the app to provide all the dependencies
    beforeEach(module('MyApp'));

    beforeEach(inject(function($controller, _$state_, _$rootScope_, _$httpBackend_) {
        ctrl = $controller('SearchController', {});
        $state = _$state_;
        $httpBackend = _$httpBackend_;
        $rootScope = _$rootScope_;

        // mocking the views 
        // (instead of including stateMock script from the post, just to simplify)
        $httpBackend.expectGET("search.html").respond("<input ng-model='vm.searchTerm'><button ng-click='vm.search()'>Search</button>");
        $httpBackend.expectGET("search-results.html").respond("<div ng-repeat='res in vm.results'> {{ res }}</div>");

        $state.go('search');
        $rootScope.$digest();
    }));

    it('should trigger a search with searchTerm "rabbit" and change state to search results page', function(){
        ctrl.searchTerm = 'rabbit';
        $state.go('search');
        ctrl.search();
        $rootScope.$digest();
        expect($state.current.name).toBe('search-results');
    })
});

Plunker

http://plnkr.co/edit/6qiYCvCu1LftLzQRyTJy?p=preview

1 个答案:

答案 0 :(得分:0)

这个答案是我在问题中 note 的另一种变体:

  

这种方法看起来更干净,选择经典   方法:将搜索服务注入状态1,获取结果,保存   他们在搜索服务中,改变状态并加载注入的结果   服务。

这意味着,当状态更改为&#39;搜索结果&#39;时,注入&#39; search_results&#39;将是一个仍然必须在第二个州解决的承诺,这意味着SearchApi尚未被调用和解决。

正如我所说的,我更喜欢在状态之间解析承诺,因为它看起来更干净,并且为了代码可读性,配置块具有您需要的所有信息。

单元测试问题解决方案:

我解决了我的主要问题,即单元测试。问题很常见,我没有包含包含父状态定义的模块。在plunker演示中,为了演示的目的,我将所有依赖项放在一个模块中,因此我在单元测试中所要做的就是包含整个模块:

beforeEach(module('MyApp'));

真正的应用程序要大得多,并且在AngularAtom—Component-based organization之后以多个模块,组件和状态分隔。

这种方法的一个好处是,引用:

  

每个州可能包含一个或多个子状态,与子组件不同,每个状态都定义自己的模块。这是因为每一个   国家,无论是父母还是孩子,都被看作是一个独立的公民   可以在申请的任何时候添加或删除   的生命周期。

这个特定的缺点使得每个状态都是一个模块方法,它使得单元测试对嵌套状态更复杂一些。

让我们说我们有三重嵌套状态(这不是那么不常见的情况):

  • STATE1
    • app.js
    • config.js
    • state1.first
      • app.js
      • config.js ...
      • state1.first.list
        • app.js
        • config.js
        • 列表controller.js
        • 列表view.html ...
      • state1.first.detail ...
    • state1.second ...

您想对list-controller.js状态下的state1.first.list进行单元测试。要从我的第一篇文章创建单元测试(测试state1.first.list中的一项功能,将状态更改为state1.first.detail状态),您的测试必须具有以下内容的导入:state1state1.firststate1.first.detail

beforeEach(module('state1'));
beforeEach(module('state1.first'));
beforeEach(module('state1.first.detail'));

原因是:每个模块都在config.js中包含它自己的状态配置。如果您尝试执行此测试,只导入state1.first.liststate1.first.detail,它将失败并且没有错误消息。 原因是它缺少父级状态配置,这是在不同的模块中定义的。 Jasmine与PhantomJS结合使用默认情况下不记录这类错误,所以这种错误有点难以发现,但这是一个不同的问题。

这些导入使得单元测试更紧密地耦合在一起,这似乎与这种模式相反,但这也是一个不同的问题。