2

Ich habe einen Komponententest, der eine Verheißungsabweisung von einem Schein nicht in der Weise verarbeitet, wie ich es erwartet hatte.Angular - Testing-Komponente - Fehler beim Zurückgeben von Promise.Reject von Mock

Ich habe diese Funktion auf einer Komponente, welche einige Daten zu addUserToOrganisation und behandelt das Versprechen sendet, dass es zurückgibt:

public onSubmit() { 
    this.saveStatus = 'Saving'; 
    this.user = this.prepareSaveUser(); 
    this._userService.addUserToOrganisation(this.user) 
    .then(() => this._router.navigate(['/profile'])) 
    .catch(error => this.reportError(error)); 
    } 

wenn diese Komponente zu testen, habe ich eine mock für UserService zur Verfügung gestellt haben, die auf der addUserToOrganisation spies Endpunkt und gibt ein Versprechen irgendeiner Art:

mockUserService = jasmine.createSpyObj('mockUserService', ['getOrgId', 'addUserToOrganisation']); 
mockUserService.getOrgId.and.returnValue(Promise.resolve('an id')); 
mockUserService.addUserToOrganisation.and.returnValue(Promise.resolve()); 

Dies funktioniert gut für glückliche Pfade (resolve) - ich kann testen, ob this._router.navigate() genannt wird und so weiter . Hier ist der bestandene Test für diesen glücklichen Weg:

it('should navigate to /profile if save is successful', fakeAsync(() => { 
    fixture.detectChanges(); 
    tick(); 
    fixture.detectChanges(); 

    component.userForm.controls['firstName'].setValue('John'); 
    component.userForm.controls['lastName'].setValue('Doe'); 
    component.userForm.controls['email'].setValue('[email protected]'); 
    component.onSubmit(); 

    tick(); 
    fixture.detectChanges(); 
    expect(mockRouter.navigate).toHaveBeenCalledWith(['/profile']); 
    })); 

Jedoch habe ich Probleme, den 'traurigen' Weg zu prüfen. Ich ändere meine mock ein Promise.reject und zurückzukehren, obwohl ich ein .catch in onSubmit habe, erhalte ich diese Fehlermeldung:

Error: Uncaught (in promise): no 

Also das ist verwirrend. Hier ist mein Test für diesen traurigen Weg. Beachten Sie, dass ich die Antwort des Scheinanrufs ändere.

it('should show Failed save status if the save function fails', fakeAsync(() => { 
    mockUserService.addUserToOrganisation.and.returnValue(Promise.reject('no')); 
    fixture.detectChanges(); 
    tick(); 
    fixture.detectChanges(); 

    component.userForm.controls['firstName'].setValue('John'); 
    component.userForm.controls['lastName'].setValue('Doe'); 
    component.userForm.controls['email'].setValue('[email protected]'); 
    component.onSubmit(); 

    tick(); 
    fixture.detectChanges(); 

    expect(component.saveStatus).toEqual('Failed! no'); 
    })); 

Hat jemand irgendwelche Ideen?

Antwort

2

Versprechen Ablehnungen sollen abgefangen werden, wenn sie nicht behandelt werden, führt dies zu einem Fehler in Zone.js Versprechungsimplementierung (die in Angular-Anwendungen verwendet wird) und einigen anderen (Chrome, Core-js versprechen Polyfill usw.).

Eine Ablehnung sollte synchron abgefangen werden, damit sie als behandelt gilt. Auf diese Weise wird garantiert, dass immer ein Fehler behandelt wird.

Dies wird behandelt Versprechen:

const p = Promise.reject(); 
p.catch(err => console.error(err)); 

Dies ist nicht behandelte Versprechen:

const p = Promise.reject(); 
setTimeout(() => { 
    p.catch(err => console.error(err)); 
}, 1000); 

Auch wenn eine Ablehnung wird möglicherweise in Zukunft behandelt werden, gibt es keine Möglichkeit, wie die Umsetzung kann ‚wissen‘, darüber , also wird ein Versprechen als unbehandelt betrachtet und unhandledrejection Ereignis ausgelöst.

Das, was das Problem schafft, ist

tick(); 
fixture.detectChanges(); 

Wenn tick() gibt es ‚nur für den Fall‘, und es gibt keinen wirklichen Gebrauch davon, sollte es in erster Linie nicht da sein. Wenn es dann Code geändert werden sollte, um keine unbehandelten Versprechen zu erstellen:

mockUserService.addUserToOrganisation.and.callFake(() => Promise.reject('no')); 
+0

Ich habe versucht, die 'tick()' zu entfernen und ich habe immer noch einen Fehler. Ich verstehe nicht ganz, wie es unnötig wäre. Allerdings habe ich meine Scheinantwort von 'returnValue ...' auf 'callFake ...' geändert. Ich glaube nicht, dass ich die Nuance hier zu 100% verstehe. – dafyddPrys

+0

'callFake' erstellt eine abgelehnte Zusage in dem Moment, in dem sie aufgerufen wird, so dass sie während des Aufrufs 'component.onSubmit()' mit '.catch' verkettet wird und zu einer Versprechung führt.'returnValue' akzeptiert existierende abgelehnte Versprechen, die sofort mit' .catch' verkettet werden sollten, aber es ist nicht - weil der Timer mit 'tick()' zum nächsten Tick bewegt wird, bevor eine Ablehnung in 'component.onSubmit()' abgefangen wurde. Das Beispiel mit 'setTimeout' zeigt genau, was dort vor sich geht. Es sollte ohne 'tick()' funktionieren. Ich kann nicht sagen, warum du diesen Fehler immer noch ohne ihn bekommst. – estus

Verwandte Themen