web-dev-qa-db-fra.com

Angular Test des unités de routeurs (états en URL)

Je rencontre une unité de test du routeur dans mon application, qui repose sur le routeur Angular ui. Ce que je veux tester, c'est si les transitions d'état changent l'URL correctement (il y aura des tests plus compliqués plus tard, mais c'est là que je commence.)

Voici la partie pertinente de mon code d'application:

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

Et le code de test:

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

Des questions similaires sur Stackoverflow (et le code de test officiel du routeur ui) suggèrent d’envelopper l’appel de $ state.go dans $ apply devrait suffire. Mais j'ai fait cela et l'état n'est toujours pas à jour. $ state.current.name reste vide.

79
Terrence

Avoir ce problème aussi, et enfin trouvé comment le faire.

Voici un exemple d'état:

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();
            }]
        }
    });
}]);

Le test unitaire ci-dessous couvrira le test de l'URL w/params et l'exécution des résolutions qui injectent leurs propres dépendances:

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

Si vous voulez vérifier uniquement le nom de l'état actuel, il est plus facile d'utiliser $state.transitionTo('splash')

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

Je me rends compte que c'est un peu hors sujet, mais je suis venu ici de Google à la recherche d'un moyen simple de tester le modèle, le contrôleur et l'URL d'un itinéraire.

$state.get('stateName')

te donnera

{
  url: '...',
  templateUrl: '...',
  controller: '...',
  name: 'stateName',
  resolve: {
    foo: function () {}
  }
}

dans vos tests.

Ainsi, vos tests pourraient ressembler à ceci:

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
15
dchacke

Vous pouvez utiliser $state.$current.locals.globals Pour accéder à toutes les valeurs résolues (voir l'extrait de code).

// Given
$httpBackend
  .expectGET('/api/users/123')
  .respond(200, { id: 1, email: '[email protected]');
                                                       
// 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', '[email protected]');

Dans ui-router 1.0.0 (actuellement en version bêta), vous pouvez essayer d'appeler $resolve.resolve(state, locals).then((resolved) => {}) dans les spécifications. Par exemple, https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js#L29

1
luacassus

Si le contenu du modèle ne vous intéresse pas, vous pouvez simplement vous moquer de $ templateCache:

beforeEach(inject(function($templateCache) {
        spyOn($templateCache,'get').and.returnValue('<div></div>');
}
1
Jelmer Jellema

Pour un state que sans 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');
        });
    });
});

NOTE: -

Pour un état imbriqué, il peut être nécessaire de fournir plusieurs modèles. Par ex. si nous avons un état imbriqué core.public.home et que chaque state, c'est-à-dire core, core.public et core.public.home a un templateUrl défini, nous devrons ajouter $templateCache.put() pour la clé templateUrl de chaque état: -

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

J'espère que cela t'aides. Bonne chance.

1
Akash