Làm thế nào để kiểm tra đơn vị một thành phần phụ thuộc vào các tham số từ ActivateRoute?


93

Tôi đang kiểm tra đơn vị một thành phần được sử dụng để chỉnh sửa một đối tượng. Đối tượng có một duy nhất idđược sử dụng để lấy đối tượng cụ thể từ một mảng các đối tượng được lưu trữ trong một dịch vụ. Cụ thể idđược mua thông qua một tham số được chuyển qua định tuyến, cụ thể là thông qua ActivatedRoutelớp.

Hàm tạo như sau:

 constructor(private _router:Router, private _curRoute:ActivatedRoute, private _session:Session) {
}

ngOnInit() {
    this._curRoute.params.subscribe(params => {
        this.userId = params['id'];
        this.userObj = this._session.allUsers.filter(user => user.id.toString() === this.userId.toString())[0];

Tôi muốn chạy các bài kiểm tra đơn vị cơ bản trên thành phần này. Tuy nhiên, tôi không chắc về cách tôi có thể đưa idtham số vào và thành phần cần tham số này.

Nhân tiện: Tôi đã có một mô hình cho Sessiondịch vụ, vì vậy đừng lo lắng ở đó.

Câu trả lời:


135

Cách đơn giản nhất để làm điều này là chỉ cần sử dụng useValuethuộc tính và cung cấp một giá trị có thể quan sát được của giá trị bạn muốn mô phỏng.

RxJS <6

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
...
{
  provide: ActivatedRoute,
  useValue: {
    params: Observable.of({id: 123})
  }
}

RxJS> = 6

import { of } from 'rxjs';
...
{
  provide: ActivatedRoute,
  useValue: {
    params: of({id: 123})
  }
}

11
Observable.of không tồn tại đối với tôi! : S
Alejandro Sanz Díaz,

4
Nhập Quan sát từ rxjs / Quan sát
zmanc

6
Mã này gây ra lỗi này trong dự án của tôi:Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/HomeContentComponent.ngfactory.js'. at http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2605
MixerOID

5
RxJs 6 ofnên được sử dụng một mình. Ngoài ra, bạn có thể sử dụng RouterTestingModulethay vì mã của câu trả lời này.
Ben Racicot

5
@BenRacicot câu trả lời này đã được đưa ra trước khi RxJs 6 tồn tại. Ngoài ra, thay vào đó nói "làm điều này thay thế" cung cấp một câu trả lời có thể được ủng hộ trực tiếp.
zmanc

18

Tôi đã tìm ra cách để làm điều này!

ActivatedRoutelà một dịch vụ, nên một dịch vụ giả cho nó có thể được thiết lập. Hãy gọi đây là dịch vụ giả MockActivatedRoute. Chúng tôi sẽ mở rộng ActivatedRoutetrong MockActivatedRoute, như sau:

class MockActivatedRoute extends ActivatedRoute {
    constructor() {
        super(null, null, null, null, null);
        this.params = Observable.of({id: "5"});
    }

Dòng super(null, ....)khởi tạo siêu lớp, có bốn tham số bắt buộc. Tuy nhiên, trong trường hợp này, chúng ta không cần gì từ bất kỳ tham số nào trong số này, vì vậy chúng ta khởi tạo chúng thành nullcác giá trị. Tất cả những gì chúng ta cần là giá trị của paramsnó là một Observable<>. Do đó, với this.params, chúng tôi ghi đè giá trị của paramsvà khởi tạo nó thành giá trị của Observable<>tham số mà đối tượng thử nghiệm đang dựa vào.

Sau đó, như bất kỳ dịch vụ mô phỏng nào khác, chỉ cần khởi tạo nó và ghi đè trình cung cấp cho thành phần.

Chúc may mắn!


1
Tôi đang đối mặt với điều này ngay bây giờ! Tuy nhiên, tôi gặp lỗi khi cố sử dụng superhoặc Observable. Những thứ này đến từ đâu?
Aarmora

super()được tích hợp sẵn. Observablelà từ rxjs/Observablehoặc chỉ rxjstùy thuộc vào phiên bản của bạn. Bạn sẽ nhận được nó bằng cách sử dụng import {Observable} from 'rxjs'.
oooyaya

Bạn đã chấp nhận một câu trả lời và đăng một câu trả lời khác ... nếu đây là Highlander (và chỉ có thể có một), bạn "thực sự" chọn câu trả lời nào và tại sao? Đó là, tôi nghĩ rằng điều này về cơ bản giảm xuống giống như câu trả lời của zmanc, mà bạn đã chấp nhận. Bạn có tìm thấy giá trị bổ sung từ việc thiết lập mô hình phức tạp hơn [một chút] này không?
ruffin

11

Trong góc 8+ có RouterTestingModule mà bạn có thể sử dụng để có quyền truy cập vào ActivateRoute hoặc Router của thành phần. Ngoài ra, bạn có thể chuyển các tuyến đến RouterTestingModule và tạo gián điệp cho các phương thức định tuyến được yêu cầu.

Ví dụ trong thành phần của tôi, tôi có:

ngOnInit() {
    if (this.route.snapshot.paramMap.get('id')) this.editMode()
    this.titleService.setTitle(`${this.pageTitle} | ${TAB_SUFFIX}`)
}

Và trong bài kiểm tra của tôi, tôi có:

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ProductLinePageComponent ],
      schemas: [NO_ERRORS_SCHEMA],
      imports: [
        RouterTestingModule.withRoutes([])
      ],
    })
    .compileComponents()
  }))

  beforeEach(() => {
    router = TestBed.get(Router)
    route = TestBed.get(ActivatedRoute)
  })

và sau đó trong phần 'nó':

  it('should update', () => {
    const spyRoute = spyOn(route.snapshot.paramMap, 'get')
    spyRoute.and.returnValue('21')
    fixture = TestBed.createComponent(ProductLinePageComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
    expect(component).toBeTruthy()
    expect(component.pageTitle).toBe('Edit Product Line')
    expect(component.formTitle).toBe('Edit Product Line')
    // here you can test the functionality which is triggered by the snapshot
  })

Theo cách tương tự, tôi nghĩ bạn có thể kiểm tra trực tiếp paramMap thông qua phương pháp spyOnProperty của jasmine, bằng cách trả về một viên bi rxjs có thể quan sát được hoặc sử dụng. Nó có thể tiết kiệm một chút thời gian và cũng không cần phải duy trì thêm một lớp giả. Hy vọng rằng nó hữu ích và nó có ý nghĩa.


Vì vậy, tốt hơn nhiều so với việc phải duy trì thêm một mô hình và bạn có thể dễ dàng thiết lập các thông số khác nhau trong các bài kiểm tra. Cảm ơn bạn!
migg

Điều này có ích. Bạn có biết cách theo dõi các tham số khác nhau không: const dirName = this.route.snapshot.paramMap.get ('dirName'); const actionType = this.route.snapshot.paramMap.get ('actionType'); Trên bot nào sẽ do thám spyOn (route.snapshot.paramMap, 'get')? Tôi có thể chỉ định khóa để nghe không?
Speksy

Như tôi đã đề cập ở trên, tôi nghĩ bạn có thể sử dụng spyOnProperty thay vì spyOn, ví dụ spyOnProperty (route.snapshot.paramMap.get, 'dirName'). Nếu tôi chưa trả lời câu hỏi của bạn hoàn toàn, đừng ngần ngại nói cho tôi biết. Cảm ơn.
dimitris maf

10

Đây là cách tôi đã thử nghiệm nó trong góc 2.0 mới nhất ...

import { ActivatedRoute, Data } from '@angular/router';

và trong phần Nhà cung cấp

{
  provide: ActivatedRoute,
  useValue: {
    data: {
      subscribe: (fn: (value: Data) => void) => fn({
        yourData: 'yolo'
      })
    }
  }
}

Bạn có thể cung cấp mã hoàn chỉnh cho phần nhà cung cấp không?
Michael JDI

Đây là một lớp kiểm tra đơn vị hoàn chỉnh. plnkr.co/edit/UeCKnJ2sCCpLLQcWqEGX?p=catalogue
Rady

Làm thế nào để bạn kiểm tra ngừng đăng ký trong ngOnDestroy
shiva

Điều này sẽ xảy ra trong cuộc sống thực vì bạn không trả lại đăng ký và bạn sẽ không thể sử dụng lệnh gọi .unsubscribe () trong ngOnDestroy.
Quovadisqc 20/02/17

1
data: Observable.of ({yourData: 'yolo'}) sẽ hoạt động.
Quovadisqc 20/02/17

4

Chỉ cần thêm một mô hình của ActivateRoute:

providers: [
  { provide: ActivatedRoute, useClass: MockActivatedRoute }
]

...

class MockActivatedRoute {
  // here you can add your mock objects, like snapshot or parent or whatever
  // example:
  parent = {
    snapshot: {data: {title: 'myTitle ' } },
    routeConfig: { children: { filter: () => {} } }
  };
}

3

Đối với một số người làm việc trên Angular> 5, if Observable.of (); không hoạt động thì họ có thể chỉ sử dụng of () bằng cách nhập import {of} từ 'rxjs';


1

Gặp phải vấn đề tương tự khi tạo các bộ thử nghiệm cho một đường dẫn định tuyến như:

{
   path: 'edit/:property/:someId',
   component: YourComponent,
   resolve: {
       yourResolvedValue: YourResolver
   }
}

Trong thành phần, tôi đã khởi tạo thuộc tính được truyền là:

ngOnInit(): void {    
   this.property = this.activatedRoute.snapshot.params.property;
   ...
}

Khi chạy các bài kiểm tra, nếu bạn không chuyển một giá trị thuộc tính trong "useValue" của ActivateRoute, thì bạn sẽ không được xác định khi phát hiện các thay đổi bằng cách sử dụng "fixture.detectChanges ()". Điều này là do các giá trị giả cho ActiisedRoute không chứa thuộc tính params.property. Sau đó, useValue giả bắt buộc phải có các tham số đó để cố định khởi tạo 'this.property' trong thành phần. Bạn có thể thêm nó dưới dạng:

  let fixture: ComponentFixture<YourComponent>;
  let component: YourComponent;
  let activatedRoute: ActivatedRoute; 

  beforeEach(done => {
        TestBed.configureTestingModule({
          declarations: [YourComponent],
          imports: [ YourImportedModules ],
          providers: [
            YourRequiredServices,
            {
              provide: ActivatedRoute,
              useValue: {
                snapshot: {
                  params: {
                    property: 'yourProperty',
                    someId: someId
                  },
                  data: {
                    yourResolvedValue: { data: mockResolvedData() }
                  }
                }
              }
            }
          ]
        })
          .compileComponents()
          .then(() => {
            fixture = TestBed.createComponent(YourComponent);
            component = fixture.debugElement.componentInstance;
            activatedRoute = TestBed.get(ActivatedRoute);
            fixture.detectChanges();
            done();
          });
      });

Bạn có thể bắt đầu thử nghiệm, chẳng hạn như:

it('should ensure property param is yourProperty', async () => {
   expect(activatedRoute.snapshot.params.property).toEqual('yourProperty');
   ....
});

Bây giờ, giả sử bạn muốn kiểm tra một giá trị thuộc tính khác, sau đó bạn có thể cập nhật ActivateRoute giả của mình dưới dạng:

  it('should ensure property param is newProperty', async () => {
    activatedRoute.snapshot.params.property = 'newProperty';
    fixture = TestBed.createComponent(YourComponent);
    component = fixture.debugElement.componentInstance;
    activatedRoute = TestBed.get(ActivatedRoute);
    fixture.detectChanges();

    expect(activatedRoute.snapshot.params.property).toEqual('newProperty');
});

Hi vọng điêu nay co ich!


0

Đã thêm nhà cung cấp trong lớp thử nghiệm dưới dạng:

{
  provide: ActivatedRoute,
  useValue: {
    paramMap: of({ get: v => { return { id: 123 }; } })
  } 
}
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.