Javascript 角度ui路由器单元测试(状态到URL)

Javascript 角度ui路由器单元测试(状态到URL),javascript,unit-testing,angularjs,angular-ui-router,Javascript,Unit Testing,Angularjs,Angular Ui Router,在我的应用程序中对路由器进行单元测试时遇到了一些问题,该应用程序构建在Angular ui路由器上。我想测试的是状态转换是否会适当地更改URL(稍后会有更复杂的测试,但这就是我要开始的地方。) 以下是我的申请代码的相关部分: angular.module('scrapbooks') .config( function($stateProvider){ $stateProvider.state('splash', { url: "/splash/", temp

在我的应用程序中对路由器进行单元测试时遇到了一些问题,该应用程序构建在Angular ui路由器上。我想测试的是状态转换是否会适当地更改URL(稍后会有更复杂的测试,但这就是我要开始的地方。)

以下是我的申请代码的相关部分:

angular.module('scrapbooks')
 .config( function($stateProvider){
    $stateProvider.state('splash', {
       url: "/splash/",
       templateUrl: "/app/splash/splash.tpl.html",
       controller: "SplashCtrl"
    })
 })
以及测试代码:

it("should change to the splash state", function(){
  inject(function($state, $rootScope){
     $rootScope.$apply(function(){
       $state.go("splash");
     });
     expect($state.current.name).to.equal("splash");
  })
})

有关Stackoverflow(以及官方ui路由器测试代码)的类似问题建议将$state.go调用包装在$apply中就足够了。但我已经这么做了,州政府仍然没有更新$state.current.name仍然为空。

也有此问题,并最终找到了解决方法

以下是一个示例状态:

angular.module('myApp', ['ui.router'])
.config(['$stateProvider', function($stateProvider) {
    $stateProvider.state('myState', {
        url: '/state/:id',
        templateUrl: 'template.html',
        controller: 'MyCtrl',
        resolve: {
            data: ['myService', function(service) {
                return service.findAll();
            }]
        }
    });
}]);
下面的单元测试将包括测试带有参数的URL,并执行注入其自身依赖项的解析:

describe('myApp/myState', function() {

  var $rootScope, $state, $injector, myServiceMock, state = 'myState';

  beforeEach(function() {

    module('myApp', function($provide) {
      $provide.value('myService', myServiceMock = {});
    });

    inject(function(_$rootScope_, _$state_, _$injector_, $templateCache) {
      $rootScope = _$rootScope_;
      $state = _$state_;
      $injector = _$injector_;

      // We need add the template entry into the templateCache if we ever
      // specify a templateUrl
      $templateCache.put('template.html', '');
    })
  });

  it('should respond to URL', function() {
    expect($state.href(state, { id: 1 })).toEqual('#/state/1');
  });

  it('should resolve data', function() {
    myServiceMock.findAll = jasmine.createSpy('findAll').and.returnValue('findAll');
    // earlier than jasmine 2.0, replace "and.returnValue" with "andReturn"

    $state.go(state);
    $rootScope.$digest();
    expect($state.current.name).toBe(state);

    // Call invoke to inject dependencies and run function
    expect($injector.invoke($state.current.resolve.data)).toBe('findAll');
  });
});

如果只想检查当前状态的名称,则更容易使用
$state.transitiono('splash')


我意识到这有点离题,但我从谷歌来到这里,寻找一种简单的方法来测试路由的模板、控制器和URL

$state.get('stateName')
我会给你

{
  url: '...',
  templateUrl: '...',
  controller: '...',
  name: 'stateName',
  resolve: {
    foo: function () {}
  }
}
在你的测试中

因此,您的测试可以如下所示:

var state;
beforeEach(inject(function ($state) {
  state = $state.get('otherwise');
}));

it('matches a wild card', function () {
  expect(state.url).toEqual('/path/to/page');
});

it('renders the 404 page', function () {
  expect(state.templateUrl).toEqual('views/errors/404.html');
});

it('uses the right controller', function () {
  expect(state.controller).toEqual(...);
});

it('resolves the right thing', function () {
  expect(state.resolve.foo()).toEqual(...);
});

// etc

对于未经
解决的
状态

// TEST DESCRIPTION
describe('UI ROUTER', function () {
    // TEST SPECIFICATION
    it('should go to the state', function () {
        module('app');
        inject(function ($rootScope, $state, $templateCache) {
            // When you transition to the state with $state, UI-ROUTER
            // will look for the 'templateUrl' mentioned in the state's
            // configuration, so supply those templateUrls with templateCache
            $templateCache.put('app/templates/someTemplate.html');
            // Now GO to the state.
            $state.go('someState');
            // Run a digest cycle to update the $state object
            // you can also run it with $state.$digest();
            $state.$apply();

            // TEST EXPECTATION
            expect($state.current.name)
                .toBe('someState');
        });
    });
});
注意:-

对于嵌套状态,我们可能需要提供多个模板。例如,如果我们有一个嵌套状态
core.public.home
和每个
state
,即
core
core.public
core.public.home
定义了
templateUrl
,我们必须为每个状态的
templateUrl
键添加
$templateCache.put()
:-


$templateCache.put('app/templates/template1.html');
$templateCache.put('app/templates/template2.html');
$templateCache.put('app/templates/template3.html');


希望这有帮助。祝您好运。

您可以使用
$state.$current.locals.globals
访问所有解析的值(请参阅代码片段)

//给定
$httpBackend
.expectGET('/api/users/123'))
.回复(200,{id:1,电子邮件:'test@email.com');
//什么时候
$state.go('users.show',{id:123});
$httpBackend.flush();
//然后
var user=$state.$current.locals.globals['user']
expact(user).to.have.property('id',123);

expact(user).to.have.property('email','test@email.com“);
如果您对模板内容中的任何内容都不感兴趣,可以模拟$templateCache:

beforeEach(inject(function($templateCache) {
        spyOn($templateCache,'get').and.returnValue('<div></div>');
}
beforeach(注入(函数($templateCache){
spyOn($templateCache,'get')。和.returnValue(“”);
}

好的,算出来了(有点)如果我定义一个模拟路由器,使用内联模板而不是模板URL,转换就会成功。你能发布你的工作代码作为答案吗?我差不多一年前就提出了这个问题。我现在的观点是,解决这个问题的最佳方法是使用in-Karma。更具体地说,问题是如果模板下载在测试中失败(即,由于没有服务器),状态更改将失败。但是,除非您正在监视$stateChangeError事件,否则不会看到错误。然而,由于状态更改失败,将不会更新$state.current.name。非常好的帖子,节省时间如果您使用字符串定义服务,请使用get而不是invoke.expect($injector.get($state.current.resolve.data)).toBe('findAll');我按照上面的代码调整了
andReturn
,就像上面的评论中提到的那样。但是,我的$state.current.name返回一个空字符串。有人知道为什么吗?@Philip我面临着同样的问题
$state.current.name
是空字符串。@Joy@VLeong我遇到了同样的问题,然后意识到这是由于ui路由器ut造成的我正在写的是使用ES6承诺,而不是
$q
。为了
$rootScope.$digest()
来解决所有的承诺。我的案例可能非常独特,但我想我会分享以防万一。@Joy$state.current.name返回空字符串时遇到了同样的问题。我不得不将$rootScope.$digest()替换为$httpBackend.flush().在这一变化之后,我得到了我所期望的。为了保持简单,我发现这个答案是最可以接受的。有测试是很好的,但必须编写一个比我的整个ui路由定义更长的测试,只是为了测试一个端点,这是效率低下的方式。无论如何,我投票支持这一点
beforeEach(inject(function($templateCache) {
        spyOn($templateCache,'get').and.returnValue('<div></div>');
}