【问题标题】:Angular unit test :WARN: 'Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?'Angular 单元测试:WARN:'导航在 Angular 区域外触发,您是否忘记调用 'ngZone.run()'?
【发布时间】:2020-02-01 20:05:20
【问题描述】:

我正在为从数据库中删除帐户的 Angular 应用程序编写单元测试。为此,我单击删除按钮。然后在 .ts 文件上调用函数。这将通过调用 API 删除帐户。

我想编写单元测试以查看是否使用 HttpTestingModule 调用了此 API,但在删除帐户后的代码中,我使用 router.navigate 导航到不同的页面。当代码命中时,它会抱怨警告:'导航在 Angular 区域外触发,你是否忘记调用'ngZone.run()'?'

ERROR: 'Unhandled Promise rejection:', 'Cannot match any routes. URL Segment: 'accountsList'', '; Zone:', 'ProxyZone', '; Task:', 'Promise.then', '; Value:', Error: Cannot match any routes. URL Segment: 'accountsList'
Error: Cannot match any routes. URL Segment: 'accountsList'
    at ApplyRedirects.noMatchError (./node_modules/@angular/router/fesm5/router.js?:1455:16)
    at CatchSubscriber.eval [as selector] (./node_modules/@angular/router/fesm5/router.js?:1436:29)
    at CatchSubscriber.error (./node_modules/rxjs/_esm5/internal/operators/catchError.js?:40:31)
    at MapSubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)
    at MapSubscriber.Subscriber.error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:69:18)
    at MapSubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)
    at MapSubscriber.Subscriber.error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:69:18)
    at MapSubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)
    at MapSubscriber.Subscriber.error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:69:18)
    at ThrowIfEmptySubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26), 'Error: Cannot match any routes. URL Segment: 'accountsList'
    at ApplyRedirects.noMatchError (./node_modules/@angular/router/fesm5/router.js?:1455:16)
    at CatchSubscriber.eval [as selector] (./node_modules/@angular/router/fesm5/router.js?:1436:29)
    at CatchSubscriber.error (./node_modules/rxjs/_esm5/internal/operators/catchError.js?:40:31)
    at MapSubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)
    at MapSubscriber.Subscriber.error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:69:18)
    at MapSubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)
    at MapSubscriber.Subscriber.error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:69:18)
    at MapSubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)
    at MapSubscriber.Subscriber.error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:69:18)
    at ThrowIfEmptySubscriber.Subscriber._error (./node_modules/rxjs/_esm5/internal/Subscriber.js?:89:26)'

这是我尝试过的

test.spec.ts

 it ('should delete account, if account exist', ()=> {
    let spyOnDelete = spyOn (component,'deleteRecord').and.callThrough();
    fixture.detectChanges();
    component.record.accountid = "Account1";
    fixture.detectChanges();
    let deleteButtonDOM = fixture.debugElement.query(By.css('#deletebtn'));
    console.log(deleteButtonDOM.nativeElement);
    deleteButtonDOM.triggerEventHandler('click',null);
    fixture.detectChanges();
    expect(spyOnDelete).toHaveBeenCalled();  //test passes 

    const req = _HttpTestingController.expectOne('/api/accounts/'+component.record.accountid);//test fails
    expect(req.request.method).toBe("DELETE");
    req.flush({status:"SUCCESS"});
    expect(component.consoleMessages.includes("POST: SUCCESS in /api/accounts")).toBe(true);


..what must be written to 
  })

test.component.ts

  deleteRecord(id) {
    this.spinner.show();
    this.http.delete('/api/accounts/' + id)
    .subscribe(res => {
        this.spinner.hide();
        if (res['status'] == "FAILURE") {
          this.consoleMessages += "\nPOST: ERROR in /api/accounts\n" + JSON.stringify(res);  
        } else {
          this.consoleMessages += "\nPOST: SUCCESS in /api/accounts\n" + JSON.stringify(res);
          this.router.navigate(['/accountsList']);//this is creating the problem xxxxxxxxxxxxxxxx
        }
      }, (err) => {
        this.consoleMessages += "\nPOST: ERROR in /api/accounts\n" + JSON.stringify(err); 
        this.spinner.hide();
        console.log(err);
      }
    );
  }

【问题讨论】:

标签: angular typescript unit-testing


【解决方案1】:

你应该模拟你的路由器。

我使用RouterTestingModule 的方式。设置如下:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [
      RouterTestingModule.withRoutes([
        { path: "", component: AccountComponent },
        { path: "**", redirectTo: "" }
      ])
    ],
    declarations: [AccountComponent],
    providers: []
  }).compileComponents();
}));

beforeEach(() => {
  fixture = TestBed.createComponent(AccountComponent);
  component = fixture.componentInstance;
  router = TestBed.inject(Router);
  fixture.detectChanges();
});

然后在你的测试中:

it('THEN: should navigate to /accounts', () => {
      const ID_TO_DELETE = 1;
      const routerNavigateSpy = jest
        .spyOn(router, 'navigate')
        .mockImplementation(() => of(true).toPromise());
      component.deleteRecord(ID_TO_DELETE);
      expect(routerNavigateSpy).toHaveBeenCalledWith(['/accounts']);
});

奖金

这篇文章解释了 Angular 区域的工作原理:ngZone

【讨论】:

    【解决方案2】:

    警告router.navigate的最小间谍和解决方案

    describe('Component', () => {
      ... other variables
      let router: jasmine.SpyObj<Router>;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule.withRoutes([yourPaths])
          ],
          ...other declarations
        })
          .compileComponents();
      }));
    
      beforeEach(() => {
        ...other initializing stuffs
        router = TestBed.get(Router);
      });
    
      it('should navigate', () => {
         spyOn(router, 'navigate');
         component.navigateToRoute();
         expect(router.navigate).toHaveBeenCalledWith(['/expected-path']);
      });
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-24
      • 2019-04-07
      • 2020-04-29
      • 1970-01-01
      • 2020-11-13
      • 1970-01-01
      • 2022-11-10
      • 2021-05-18
      相关资源
      最近更新 更多