angularcypresscypress-component-test-runner

Cypress spy no being called


I'm trying to create a simple Cypress test that tests whether a service method was called. Here is my test setup:

const accountService = {
   logout: () => {
      return null;
   }
}
beforeEach(() => {
   cy.mount(ToolbarComponent, {
      imports: [
         MatDialogModule,
         MatMenuModule,
         MatToolbarModule,
         MatButtonModule,
         BrowserAnimationsModule,
         MatIconModule
     ],
     declarations: [ToolbarComponent],
     providers: [{provide: AccountService, useValue: accountService}]
  });
});

describe('logout method', () => {
     it('should call the account service logout method', () => {
         cy.spy(accountService, 'logout').as('accountService-logout-method');
         cy.get(elementBindings.userMenuButton).click();
         cy.wait(1000);
         cy.get(elementBindings.logoutButton).should('be.visible'); // THIS PASSES
         cy.get(elementBindings.logoutButton).click();
         expect(accountService.logout).to.be.called; 
     });
});

This is the relevant html:

<button
  data-cy="open-user-menu-button"
  mat-icon-button
  class="example-icon favorite-icon"
  aria-label="Example icon-button with heart icon"
  [matMenuTriggerFor]="menu">
  <mat-icon>person</mat-icon>
</button>
<mat-menu #menu="matMenu">
  <button mat-menu-item>Edit Profile</button>
  <button data-cy='logout-button' *ngIf="loggedOnUser$ | async" mat-menu-item (click)="logout()">Logout</button>
</mat-menu>

This is the logout() method in the component.ts file:

logout() {    
   this.accountService.logout();
}

The last expect statement: expect(accountService.logout).to.be.called; is failing and I cannot understand why? The expect() statement to verify the button is visible passes and I can physically see the button is there in Cypress before the click() method is called on it.

Any ideas why this might be happening?


Solution

  • So the main reason you're likely having an issue is because Cypress is asynchronous. So using the expect() outside of a then() or should() callback won't work since you have to wait for the Cypress commands to complete.

    I see you already have an alias on your spy, so to assert a spy:

    // Instead of this
    expect(accountService.logout).to.be.called;
    
    // Get the alias, then use should() to assert
    cy.get('@accountService-logout-method').should('have.been.called');
    
    // or 
    cy.get('@accountService-logout-method').should((spy) => {
      expect(spy).to.be.called;
    });
    

    Your entire test would look something like this:

    // Entire test
    it('should call the account service logout method', () => {
      cy.spy(accountService, 'logout').as('accountService-logout-method');
      cy.get(elementBindings.userMenuButton).click();
      cy.wait(1000);
      cy.get(elementBindings.logoutButton).should('be.visible'); // THIS PASSES
      cy.get(elementBindings.logoutButton).click();
    
      cy.get('@accountService-logout-method').should('have.been.called');
    });