Angular uiルーターユニットテスト(状態からURL)


81

Angularuiルーター上に構築されたアプリケーションでルーターの単体テストに問題が発生しています。私がテストしたいのは、状態遷移が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は空のままです。


わかりました(ある種)。テンプレートURLの代わりにインラインテンプレートを使用してモックルーターを定義すると、移行は成功します。
テレンス

答えとして作業コードを投稿できますか?
Levi Hackwith 2014年

2
私はほぼ1年前にこの質問をしました。私の見解では、この問題を解決する最善の方法は、Karmaでng-template-to-jsプリプロセッサを使用することです。
テレンス2014

具体的には、テストでテンプレートのダウンロードが失敗した場合(つまり、サーバーがないため)、状態の変更が失敗するという問題があります。ただし、$ stateChangeErrorイベントを監視していない限り、エラーは表示されません。それでも、状態の変更が失敗するため、$ state.current.nameは更新されません。
テレンス2015年

回答:


125

この問題も抱えていて、ついにその方法を見つけました。

状態の例を次に示します。

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');
  });
});

1
文字列を使用してサービスを定義している場合は、時間の節約になります。invokeの代わりにgetを使用してください。expect($ injector.get($ state.current.resolve.data))。toBe( 'findAll');
Alan Quigley 2014年

2
andReturnは上記のコメントに記載されているように調整して上記のコードに従いました。ただし、$ state.current.nameは空の文字列を返します。誰もが理由を知っていますか?
Vicky Leong 2015年

5
@Philip同じ問題に直面しているの$state.current.nameは空の文字列です。
ジョイ

1
@Joy @VLeong私はこれと同じ問題を抱えていましたが、私が書いているui-routerユーティリティがの代わりにES6promiseを使用していたことが原因であることに気付きました$q。すべてが使用しなければならない$qために約束を$rootScope.$digest()すべての約束を解決するために。私のケースはおそらくかなりユニークですが、念のために共有したいと思いました。
スティグラー2015

1
@Joy $ state.current.nameが空の文字列を返すという同じ問題が発生していました。$ rootScope。$ digest()を$ httpBackend.flush()に置き換える必要がありました。その変更に続いて、私は私が期待したものを手に入れました。
ジェイソンブキャナン

18

現在の状態の名前だけを確認したい場合は、より簡単に使用できます $state.transitionTo('splash')

it('should transition to splash', inject(function($state,$rootScope){
  $state.transitionTo('splash');
  $rootScope.$apply();
  expect($state.current.name).toBe('splash');
}));

4
簡単にするために、私はこの答えが最も受け入れられると思います。テストを行うことは素晴らしいことですが、単一のエンドポイントをテストするためだけにui-route定義全体よりも長いテストを作成する必要があるのは、非効率的な方法です。いずれにせよ、私はこれに投票します
Thomas

15

これは少し話題から外れていることに気づきましたが、ルートのテンプレート、コントローラー、URLをテストする簡単な方法を探してGoogleからここに来ました。

$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

1

そのために: stateなしresolve

// 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、すなわちcorecore.publiccore.public.homeしているtemplateUrl定義され、我々は、追加する必要があります$templateCache.put()各状態のためtemplateUrlのキー: -

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

お役に立てれば。幸運を。


1

を使用$state.$current.locals.globalsして、解決されたすべての値にアクセスできます(コードスニペットを参照)。

// Given
$httpBackend
  .expectGET('/api/users/123')
  .respond(200, { id: 1, email: 'test@email.com');
                                                       
// When                                                       
$state.go('users.show', { id: 123 });
$httpBackend.flush();                            
       
// Then
var user = $state.$current.locals.globals['user']
expact(user).to.have.property('id', 123);
expact(user).to.have.property('email', 'test@email.com');

ui-router 1.0.0(現在はベータ版)$resolve.resolve(state, locals).then((resolved) => {})では、仕様で呼び出すことができます。たとえば、https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js#L29


1

テンプレートのコンテンツに興味がない場合は、$ templateCacheをモックするだけです。

beforeEach(inject(function($templateCache) {
        spyOn($templateCache,'get').and.returnValue('<div></div>');
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.