Kiểm tra đơn vị định tuyến ui góc (trạng thái thành url)


81

Tôi đang gặp sự cố với đơn vị kiểm tra bộ định tuyến trong ứng dụng của mình, bộ định tuyến này được xây dựng trên bộ định tuyến Angular ui. Điều tôi muốn kiểm tra là liệu quá trình chuyển đổi trạng thái có thay đổi URL thích hợp hay không (sẽ có nhiều bài kiểm tra phức tạp hơn sau này, nhưng đây là lúc tôi bắt đầu.)

Đây là phần có liên quan trong mã ứng dụng của tôi:

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

Và mã thử nghiệm:

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

Các câu hỏi tương tự trên Stackoverflow (và mã kiểm tra chính thức của bộ định tuyến ui) đề xuất gói lệnh gọi $ state.go trong $ apply là đủ. Nhưng tôi đã làm điều đó và trạng thái vẫn không cập nhật. $ state.current.name vẫn trống.


Được rồi, đã tìm ra (đại loại là.) Nếu tôi xác định một bộ định tuyến giả, với các mẫu nội tuyến thay vì URL mẫu, thì quá trình chuyển đổi sẽ thành công.
Terrence

Bạn có thể đăng mã làm việc của mình làm câu trả lời không?
Levi Hackwith

2
Tôi đã hỏi câu hỏi này gần một năm trước. Quan điểm của tôi bây giờ là cách tốt nhất để giải quyết vấn đề này là sử dụng bộ tiền xử lý ng-template-to-js trong Karma.
Terrence

Cụ thể hơn: vấn đề là nếu quá trình tải xuống mẫu không thành công trong quá trình thử nghiệm (tức là vì không có máy chủ), thì việc thay đổi trạng thái sẽ không thành công. Tuy nhiên, trừ khi bạn đang theo dõi sự kiện $ stateChangeError, bạn sẽ không thấy lỗi. Tuy nhiên, vì thay đổi trạng thái không thành công, $ state.current.name sẽ không được cập nhật.
Terrence

Câu trả lời:


125

Tôi cũng đang gặp vấn đề này, và cuối cùng đã tìm ra cách làm.

Đây là một trạng thái mẫu:

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

Bài kiểm tra đơn vị bên dưới sẽ bao gồm việc kiểm tra URL w / params và thực thi các giải pháp đưa vào các phụ thuộc của chính nó:

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
Bài đăng tuyệt vời, tiết kiệm thời gian nếu bạn đang sử dụng chuỗi để xác định một dịch vụ, hãy sử dụng get thay vì gọi. mong đợi ($ injectionor.get ($ state.current.resolve.data)). toBe ('findAll');
Alan Quigley

2
Tôi đã làm theo mã trên với các điều chỉnh để andReturnthích được đề cập trong nhận xét ở trên. Tuy nhiên, $ state.current.name của tôi trả về một chuỗi trống. Có ai biết tại sao không?
Vicky Leong

5
@Philip Tôi đang đối mặt với vấn đề tương tự $state.current.namelà chuỗi trống.
Joy

1
@Joy @VLeong Tôi cũng gặp sự cố này, sau đó nhận ra rằng đó là do tiện ích ui-router mà tôi đang viết đang sử dụng các hứa hẹn của ES6 thay vì $q. Mọi thứ phải sử dụng $qlời hứa $rootScope.$digest()để giải quyết tất cả các lời hứa. Trường hợp của tôi có lẽ khá độc đáo, nhưng tôi nghĩ tôi sẽ chia sẻ để đề phòng.
Stiggler,

1
@Joy Tôi gặp vấn đề tương tự với $ state.current.name trả về một chuỗi trống. Tôi đã phải thay thế $ rootScope. $ Digan () bằng $ httpBackend.flush (). Sau sự thay đổi đó, tôi đã có được những gì tôi mong đợi.
Jason Buchanan

18

Nếu bạn chỉ muốn kiểm tra tên của trạng thái hiện tại, nó dễ sử dụng hơn $state.transitionTo('splash')

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

4
Để đơn giản, tôi thấy câu trả lời này là dễ chấp nhận nhất. Có bài kiểm tra là tốt và tất cả, nhưng phải viết một bài kiểm tra dài hơn thì toàn bộ định nghĩa ui-route của tôi chỉ để kiểm tra một điểm cuối duy nhất chỉ là một cách không hiệu quả. Trong mọi trường hợp, tôi sẽ bỏ phiếu cho điều này
Thomas

15

Tôi nhận thấy điều này hơi lạc đề, nhưng tôi đến đây từ Google để tìm kiếm một cách đơn giản để kiểm tra mẫu, bộ điều khiển và URL của tuyến đường.

$state.get('stateName')

sẽ cho bạn

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

trong các bài kiểm tra của bạn.

Vì vậy, các bài kiểm tra của bạn có thể trông giống như sau:

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

Đối với stateđiều đó mà không có 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');
        });
    });
});

GHI CHÚ:-

Đối với trạng thái lồng nhau, chúng tôi có thể cần cung cấp nhiều hơn một mẫu. Đối với người yêu cũ. nếu chúng ta có một nhà nước lồng nhau core.public.homevà mỗi state, tức là core, core.publiccore.public.homecó một templateUrlđịnh nghĩa, chúng ta sẽ có thêm $templateCache.put()cho mỗi bang templateUrlchủ chốt: -

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

Hi vọng điêu nay co ich. Chúc may mắn.


1

Bạn có thể sử dụng $state.$current.locals.globalsđể truy cập tất cả các giá trị đã phân giải (xem đoạn mã).

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

Trong ui-router 1.0.0 (hiện đang là phiên bản beta), bạn có thể thử gọi $resolve.resolve(state, locals).then((resolved) => {})trong phần thông số kỹ thuật. Ví dụ: https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js#L29


1

Nếu bạn không quan tâm đến bất kỳ điều gì trong nội dung của mẫu, bạn có thể chỉ cần giả lập $ templateCache:

beforeEach(inject(function($templateCache) {
        spyOn($templateCache,'get').and.returnValue('<div></div>');
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.