typescript - Stubba ut Angular-tjänster i Cypress


Jag har denna Angular-webbapplikation som jag vill köra e2e-tester på mot en mockad REST API. Jag kan stubba ut mina nätverksförfrågningar till min REST API tillräckligt enkelt, men autentiseringen använder en tredjepartsleverantör (Cognito med Amplify).

Nu vill jag stubba ut Angular-tjänsten som kapslar in autentiseringen.

I Angular har jag

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  some methods

  isSignedIn(): Observable<boolean> {
    ...
  }
}

Jag vill stubba isSignedIn()-metoden. Mitt första försök ser ut ungefär så här:

import {AuthenticationService} from "../../src/app/authentication.service";
import {BehaviorSubject} from "rxjs";

context('albums', () => {

  it('get albums', () => {
    cy.stub(AuthenticationService,'isSignedIn').returns(new BehaviorSubject(true));
  }
}

Cypress/Chrome klagar sedan på att det inte kan hitta AuthenticationService på den platsen. Hur löser jag detta?

Svar #1

Det finns några problem med ditt test

1 - Cypress själv körs inte inom webbläsaren, det är en wrapper-applikation som styr webbläsaren. Den använder JS-språk, men på wrappern och inte inom webbläsaren. Den kan dock skicka JS-kommandon till webbläsaren, som jag kommer att förklara nedan.

2 - AuthenticationService-klassen i ditt Cypress-test är annorlunda än den som används av Angular... av två skäl:

  • När vi importerar en klass till Cypress-testet kommer Cypress att använda sin förbehandlare för att bygga en ny js-klass från ts-filen; Denna klass kommer att vara ett annat objekt än det som Angular skapar med sin förbehandlare.
  • klassen som importeras till testet kommer att laddas i Cypress-scope (inte i webbläsarens där Angular är laddat)

3 - även om AuthenticationService hade samma scope som den i Angular, skulle det fortfarande vara fel... det du behöver är instansen av denna klass från inom Angulas scope/zone.

Oroa dig inte, Cypress låter dig nå webbläsarens omfång med dess Window-funktion:

cy.window()
      .then((window) => { // the window here is the browser's
        window['console'].log('Hi there from Cypress scope'); // this will appear in the console window within cypress
      })
});

Och vi skulle kunna tillhandahålla en referens till tjänsten från Angulars scope till webbläsarens fönster. Till exempel, genom att använda din AppComponent på detta sätt

export class AppComponent {
  constructor(..., authenticationService: AuthenticationService) {
    window['authenticationService'] = authenticationService;
  }
}

och genom att använda Cypresses Window-funktion för att få webbläsarens window-objekt, kan ditt test sedan stubba tjänsten:

cy.window().then((window) => {
  const serviceFromAngularScope = window['authenticationService'];

  cy.stub(serviceFromAngularScope,'isSignedIn').returns(new BehaviorSubject(true));
});

Förhoppningsvis får detta dig att komma igång

Svar #2

Jag håller i princip med om det som har sagts. Cypress är ett ramverksoberoende UI-testverktyg. Till skillnad från Jasmine/Jest som kan arbeta med Angular/React för att mocka tjänster, fungerar det på ett annat sätt.

Med det sagt har det problem, alltså behöver vissa lösningar när man kör tester över domäner som involverar OIDC- och OAuth-autentiseringsflöden.

Kanske kan du försöka titta på cookies/sessionStorages före och efter lyckad inloggning under Chrome DevTools, om du kan ta dig förbi autentiseringsflödet:

cy.window().then(window => {
  window.sessionStorage.setItem(key, value);
})

Relaterade artiklar