Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stubbing out Angular services in Cypress

I have this Angular web application I want to run e2e tests on a mocked out REST API. I can stub out my network requests to my REST API easy enough, but the authentication is using a third-party provider (Cognito using Amplify).

Now I want to stub out the Angular service that wraps the authentication.

In Angular I have

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

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

I want to stub the isSignedIn()-method. My first attempt looks something like this:

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 then complains it cannot find AuthenticationService on that location. How do I solve this?

like image 772
rd31415 Avatar asked Oct 05 '19 16:10

rd31415


People also ask

What does stubbing mean in Cypress?

Replace a function, record its usage and control its behavior.

Does Cypress support angular applications?

The Cypress setup in an Angular workspace is easy to do because of the available Cypress schematic library. The @briebug/cypress-schematic project adds Cypress to your workspace and configures the angular.

How we can mock the data in Cypress?

There are several ways in Cypress to use JSON files for mocking data. Cypress uses so-called fixtures for that. By default, it will generate a fixtures folder where you can store your JSON mock files.


2 Answers

I basically agree what has been said. Cypress is a framework independent ui testing tool. Unlike Jasmin/Jest that can work with angular/react to mock services, it works differently.

With that being said, it has problems aka need certain work arounds executing tests cross domain when involving OIDC, OAuth authentication flows.

Maybe you can try looking into cookies / sessionStorages before and after successful login under chrome devtools, if you can get past the authentication flow:

cy.window().then(window => {
  window.sessionStorage.setItem(key, value);
})
like image 115
Yuqiu G. Avatar answered Oct 22 '22 23:10

Yuqiu G.


There are a few issues with your test

1 - Cypress itself is not executed within the browser, it is a wrapper application that commands the browser. It uses JS language, but on the wrapper not withing the browser. It can send JS commands to the browser though, as I will explain below.

2 - the AuthenticationService class in your cypress test is different from the one used by angular... for two reasons:

  • When we import a class into the Cypress test, Cypress will use its pre-processor to build a new js class out of the ts file; This class will be a different object from the one angular creates with its pre-processor.
  • the class import into the test will be loaded in the cypress scope (not the browser's one where angular is loaded)

3 - even if AuthenticationService was the same scope as the angular one, it would still be incorrect... what you need is the instance of this class from within Angular's scope/zone.

Worry not, Cypress allows you to reach into the Browser's scope with its Window function:

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

And we could provide a reference to the service from within angular's scope to the browser's window. For example, from using your AppComponent like this

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

and using cypress's Window function to get the browser's window object, your test could then stub the service:

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

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

Hopefully this gets you started

like image 1
The Fabio Avatar answered Oct 22 '22 23:10

The Fabio