Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't find button inside a *ngIf statement while testing because statement involves a mocked class

I'm building an application where people can authenticate with Github in Angular. I've build a button to do so. When trying to test this button, the test doesn't seem to find the button. I think it has to do something with the *ngIf statement.

login.component.spec.ts:

    class MockAuthService implements Partial<AuthService> {
  isAuthenticated() {
    return 'Mocked';
  }


  loginwithGithubProvider() {
    return new Promise((resolve, reject) => resolve());
  }

  logout(): Promise<boolean | Observable<never> | never> {
    return new Promise(function(resolve, reject) { resolve(); });
  }
}


describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;
  let componentService: AuthService;
  const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
  const ghServiceSpy = jasmine.createSpyObj('GithubService', ['methodName']);

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        MatCardModule,
        AngularFireModule.initializeApp(environment.firebase),
        RouterModule.forRoot([{path: '', component: LoginComponent}]),
      ],
      declarations: [
        LoginComponent
      ],
      providers: [
        AuthService,
        AngularFirestore,
        AngularFireAuth,
        HttpClient,
        HttpHandler
      ]
    });
    TestBed.overrideComponent(LoginComponent, {
      set: {
        providers: [
          {provide: AuthService, useClass: MockAuthService},
          {provide: Router, useValue: routerSpy},
          {provide: GithubService, useValue: ghServiceSpy},
        ]
      }
    });

    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    componentService = fixture.debugElement.injector.get(AuthService);
  }));

  it('Logginbutton calls signInWithGithub', async(() => {
    spyOn(component, 'signInWithGithub');
    const button =  fixture.debugElement.nativeElement.querySelector('#signInWithGithub');
    button.click();
    fixture.whenStable().then(() => {
      expect(component.signInWithGithub).toHaveBeenCalled();
    });
  }));

login.component.html:

 <div *ngIf="authService.isLoggedIn; else loginTemplate">
    <h1>Welcome {{authService?.username}}</h1>
    <img src="{{ authService?.userDetails?.photoURL }} ">
    <br />
    <button mat-flat-button class="btn-github" id="logout" (click)="logout()">
      <i class="fab fa-github"></i> Stop authentication Github
    </button>
  </div>

  <ng-template #loginTemplate>
    <h1>FullProjectCloner</h1>
    <button mat-flat-button class="btn-github" id="signInWithGithub" (click)="signInWithGithub()">
      <i class="fab fa-github"></i> Authenticate with github
    </button>
    <p class="authError">{{this?.loginError}}</p>
  </ng-template> 

The error i get:

TypeError: Cannot read property 'click' of null
like image 769
Tim MG Avatar asked Oct 12 '25 13:10

Tim MG


1 Answers

The issue is you have set everything up, but not invoked the lifecycle changes necessary to have Angular actually render anything before you are testing it.

Simple fix is to invoke fixture.detectChanges() after you spyOn the component.

I put this together in a Stackblitz to be sure it would work for you. In that Stackblitz simply comment out the fixture.detectChanges() and you will see the issue.

Here is the spec from that Stackblitz:

it('Logginbutton calls signInWithGithub', async(() => {
  spyOn(component, 'signInWithGithub');
  fixture.detectChanges();
  const button =  fixture.debugElement.nativeElement.querySelector('#signInWithGithub');
  button.click();
  expect(component.signInWithGithub).toHaveBeenCalled();
}));

You will notice I also removed the call to whenStable() as that also is not needed.

like image 77
dmcgrandle Avatar answered Oct 14 '25 16:10

dmcgrandle



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!