Açısal kullanıcı arabirimi yönlendirici birim testi (URL'leri belirtir)


81

Angular ui yönlendiricisi üzerine kurulu uygulamamdaki yönlendiriciyi test ederken bazı sorunlar yaşıyorum. Test etmek istediğim şey, durum geçişlerinin URL'yi uygun şekilde değiştirip değiştirmediğidir (daha sonra daha karmaşık testler olacak, ancak burada başlıyorum.)

İşte uygulama kodumun ilgili kısmı:

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

Ve test kodu:

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 (ve resmi kullanıcı arabirimi yönlendirici test kodu) ile ilgili benzer sorular, $ state.go çağrısının $ apply içine kaydırılması yeterli olacaktır. Ama bunu yaptım ve durum hala güncellenmiyor. $ state.current.name boş kalır.


Tamam, anladım (bir nevi). Şablon URL'leri yerine satır içi şablonlarla bir sahte yönlendirici tanımlarsam, geçiş başarılı olur.
Terrence

Cevap olarak çalışma kodunuzu gönderebilir misiniz?
Levi Hackwith

2
Bu soruyu neredeyse bir yıl önce sordum. Şimdi benim görüşüme göre, bu sorunu çözmenin en iyi yolu, Karma'da ng-şablon-js ön işlemcisini kullanmaktır.
Terrence

Daha spesifik olarak: sorun, eğer şablon indirmesi testte başarısız olursa (yani, sunucu olmadığı için) durum değişikliğinin başarısız olacağıdır. Ancak, $ stateChangeError olayını izlemiyorsanız, hatayı görmezsiniz. Yine de, durum değişikliği başarısız olduğu için, $ state.current.name güncellenmeyecektir.
Terrence

Yanıtlar:


125

Bu sorunu da yaşadım ve sonunda nasıl yapılacağını anladım.

İşte örnek bir durum:

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

Aşağıdaki birim testi, URL'nin parametrelerle test edilmesini ve kendi bağımlılıklarını enjekte eden çözümlerin yürütülmesini kapsayacaktır:

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
Harika gönderi, zaman kazandıran bir hizmeti tanımlamak için dizeleri kullanıyorsanız, çağırmak yerine get kullanın. beklemek ($ injector.get ($ state.current.resolve.data)). toBe ('findAll');
Alan Quigley

2
Yukarıdaki andReturnyorumda belirtildiği gibi ayarlamalar yaparak yukarıdaki kodu takip ettim . Ancak, $ state.current.name boş bir dize döndürüyor. Nedenini bilen var mı?
Vicky Leong

5
@Philip Aynı sorunla karşı karşıyayım $state.current.nameboş dizedir.
Joy

1
@Joy @VLeong Aynı sorunu yaşadım, sonra yazdığım ui-yönlendirici yardımcı programından kaynaklandığını fark ettim, bunun yerine ES6 vaatleri kullanıyordu $q. Tüm sözlerin yerine $qgetirilmesi için her şeyin sözler kullanması gerekir $rootScope.$digest(). Davam muhtemelen oldukça benzersizdir, ancak her ihtimale karşı paylaşacağımı düşündüm.
Stiggler

1
@Joy $ state.current.name ile aynı sorunu boş bir dizge döndürürken alıyordum. $ RootScope. $ Digest () 'i $ httpBackend.flush () ile değiştirmek zorunda kaldım. Bu değişikliğin ardından beklediğim şeyi aldım.
Jason Buchanan

18

Yalnızca mevcut eyaletin adını kontrol etmek istiyorsanız, kullanımı daha kolay $state.transitionTo('splash')

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

4
Basit tutmak için bu cevabı en kabul edilebilir buluyorum. Teste sahip olmak güzel bir şey, ancak tek bir son noktayı test etmek için tüm ui-rotası tanımımdan daha uzun olan bir test yazmak, verimsiz olmanın bir yoludur. Her halükarda buna oy veriyorum
Thomas

15

Bunun biraz konu dışı olduğunun farkındayım, ancak buraya Google'dan bir rotanın şablonunu, denetleyicisini ve URL'sini test etmenin basit bir yolunu aramaya geldim.

$state.get('stateName')

sana vereceğim

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

testlerinizde.

Yani testleriniz şöyle görünebilir:

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

A için stateolmadan 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');
        });
    });
});

NOT:-

İç içe geçmiş bir durum için birden fazla şablon sağlamamız gerekebilir. Örn. Biz iç içe bir devlet varsa core.public.homeve her stateyani core, core.publicve core.public.homebir etmiştir templateUrltanımlanmış, biz eklemek zorunda kalacak $templateCache.put()her devletin için templateUrlanahtarın: -

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

Bu yardımcı olur umarım. İyi şanslar.


1

$state.$current.locals.globalsÇözülen tüm değerlere erişmek için kullanabilirsiniz (kod parçacığına bakın).

// 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'da (şu anda beta) $resolve.resolve(state, locals).then((resolved) => {}), özelliklerde çağırmayı deneyebilirsiniz . Örneğin https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js#L29


1

Şablonun içeriğiyle ilgilenmiyorsanız, sadece $ templateCache ile alay edebilirsiniz:

beforeEach(inject(function($templateCache) {
        spyOn($templateCache,'get').and.returnValue('<div></div>');
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.