简体   繁体   English

使用Angular.js + UI路由器进行Karma测试

[英]Karma Testing with Angular.js + UI Router

I was wondering how should one test with Karma test with Angular.js + UI router? 我想知道如何使用Angular.js + UI路由器进行Karma测试?

I have the following state defined: Which has two resolves that fetches some data and prepares the data for the controller. 我定义了以下状态:哪个有两个解析器来获取一些数据并为控制器准备数据。 (Coming from Ember background, this makes a lot of sense.) (来自Ember背景,这很有道理。)

$stateProvider
  .state('users', {
    resolve: {
      getData: function (User) {
        return User.query().$promise
      },
      stateModels: function (getData) {
        var models = {}
        models.users = getData
        return models
      }
    },
    url: '/',
    templateUrl: '/views/users/index.html',
    controller: 'UsersIndexCtrl'
  })

Our UserIndexCtrl looks like: (Which takes the resolved stateModels and assigns it to the $scope, so the view can use it) 我们的UserIndexCtrl看起来像:(它接受已解析的stateModels并将其分配给$ scope,因此视图可以使用它)

app.controller('UsersIndexCtrl', [
  '$scope', '$state', 'stateModels',
  function ($scope, $state, stateModels) {

    $scope.users = stateModels.users

  }])

This is working great in the browser, I am seeing the right results. 这在浏览器中运行良好,我看到了正确的结果。 However, when it comes to testing it is giving me odd errors. 但是,当涉及到测试时,它给了我奇怪的错误。

Here is an example Karma unit test: 以下是Karma单元测试的示例:

describe('controllers', function () {

  var $httpBackend
    , $rootScope
    , $scope
    , $state
    , $httpBackend
    , $controller

  beforeEach(module('app'))

  beforeEach(inject(function ($injector) {
    $state = $injector.get('$state')
    $rootScope = $injector.get('$rootScope')
    $httpBackend = $injector.get('$httpBackend')
    $scope = $rootScope.$new()
    $controller = $injector.get('$controller')
  }))

  it('UserIndexCtrl should exist', inject(function () {
    $httpBackend
      .expect('GET', '/api/users')
      .respond(200, {users: [ {}, {}, {} ]})

    $state.go('users')
    $rootScope.$apply()

    $controller('AdminZonesIndexCtrl', { $scope: $scope, $state: $state });
    $rootScope.$apply()
    assert.equal($scope.users.length, 3)
  }))

})

And I am seeing: 而且我看到:

[$injector:unpr] Unknown provider: stateModelsProvider <- stateModels
http://errors.angularjs.org/1.3.0-build.2937+sha.4adc44a/$injector/unpr?p0=stateModelsProvider%20%3C-%20stateModels
Error: [$injector:unpr] Unknown provider: stateModelsProvider <- stateModels
http://errors.angularjs.org/1.3.0-build.2937+sha.4adc44a/$injector/unpr?p0=stateModelsProvider%20%3C-%20stateModels

The idea here is: 这里的想法是:

  • We mock out the API request so that GET requests to /api/users will return an array of 3 objects. 我们模拟出API请求,以便对/ api / users的GET请求将返回3个对象的数组。
  • We go to the state named users 我们去名为users的州
  • We expect to see that the $scope.users should be an array of 3 objects. 我们希望看到$ scope.users应该是一个包含3个对象的数组。
  • From this test, we tested both the resolves defined in the router, and that the controller assigned the resolved objects correctly. 通过此测试,我们测试了路由器中定义的两个解析,以及控制器正确分配了已解析的对象。

Thanks Bill 谢谢比尔

The reason for your error is that you are first transitioning to a state which instantiates your UsersIndexCtrl with a new scope, but then subsequently creating another instance of the controller (again, with a new scope) within the test. 出现错误的原因是您首先转换到使用新作用域实例化UsersIndexCtrl的状态,然后在测试中创建另一个控制器实例(同样,使用新作用域)。 The two are independent of one another, and in the second instance, stateModels is an unknown/unavailable dependency. 这两者是彼此独立的,在第二种情况下, stateModels是未知/不可用的依赖。

So, while your ideas are valid testing concerns, in trying to assert all three together you are essentially performing an end-to-end test in a unit test environment. 因此,虽然您的想法是有效的测试问题,但在尝试将所有三个一起断言时,您实际上是在单元测试环境中执行端到端测试。 You should not want to - doing so will create a brittle dependency on: 你不应该这样做 - 这样做会产生一种脆弱的依赖:

  • the controller belonging to the "users" state 属于“用户”状态的控制器
  • that somewhere in the state transition a particular http request is called 在状态转换的某个地方调用特定的http请求
  • that the stateModels dependency is bound to the scope. stateModels依赖项绑定到范围。

Of these assertions, only the last is of any concern to the controller. 在这些断言中,只有最后一个与控制器有任何关系。 Unit tests for your controller don't care how/when it was instantiated or where the stateModels dependency came from, they are only concerned with the controller's own behaviour. 控制器的单元测试不关心实例化的方式/时间或stateModels依赖关系的来源,它们只关心控制器自身的行为。 So, let's split this behaviour up: 那么,让我们分开这个行为:

Unit testing the controller 单元测试控制器

Your first test should be reduced to the following: 您的第一次测试应该减少到以下几点:

it('binds the users to the scope', function(){
   var stateModels = [{}, {}, {}];
   $controller('UserIndexCtrl', {$scope: $scope, stateModels: stateModels});
   assert.equal($scope.users, stateModels);
});

Note that as you add more tests, you will likely want to move your controller instantiation to a beforeEach block. 请注意,在添加更多测试时,您可能希望将控制器实例化移动到beforeEach块。

Testing the route 测试路线

The concern of testing a route is really that of application behaviour, for which you should defer to Protractor . 测试路径的关注点实际上是应用程序行为的问题,您应该遵循Protractor However, if you particularly want to perform a unit test on a state, this is perhaps best served by testing the configuration of the state itself. 但是,如果您特别想对状态执行单元测试,则最好通过测试状态本身的配置来实现。 For example: 例如:

it('resolves the stateModels dependency', function() {
   var state = $state.get('users');
   assert.isDefined(state.resolve.stateModels); 
   // perform assertion that stateModels function resolves to what is expected
   // Note: any such assertion should stub any dependency being used, to ensure
   // we are testing in isolation.
});

That notwithstanding, I personally don't elect to unit test routing/route configuration, and instead acquire such coverage through e2e testing with Protractor. 尽管如此,我个人并不选择单元测试路由/路由配置,而是通过使用Protractor的e2e测试获得此类覆盖。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM