1

I'm trying to test a button event component call through the html. The button works when run manually. I can successfully test the component directly. I can also successfully test the button renders, But the test is not seeing the function call when executed through the html.

Html:

<div class="row">
    <button id="homeBtn" class="btn btn-primary" [routerLink]="['home']">Home</button>
  </div>

Component Code:

export class AppComponent {
  title = 'My app';
  constructor(private router: Router) {}

  goHome() {
    this.router.navigate(['./']);
  }

Test Spec:

 beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [
            AppComponent,
            HomeComponent,
          ],
          imports: [
            FormsModule,
            RouterModule.forRoot(ROUTES),
            RouterTestingModule.withRoutes([])
          ],
          providers: [
            { provide: APP_BASE_HREF, useValue: '/' }
          ]
        }).compileComponents();
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.debugElement.componentInstance;
        instance = fixture.debugElement.nativeElement;
      }));

// this test works
it('home button should work', () => {
    spyOn(component, 'goHome');
    component.goHome();
    expect(component.goHome).toHaveBeenCalled();
  });

  // this test works
  it('should render the HOME button', async(() => {
    spyOn(component, 'goHome');
    fixture.detectChanges();
    let button = instance.querySelector('#homeBtn');
    expect(button.textContent).toContain('Home', 'button renders');
  }));

  // this test fails
 it('should call goHome function', async(() => {
    spyOn(component, 'goHome');
    fixture.detectChanges();
    let button = instance.querySelector('#homeBtn');
    button.click();
    fixture.detectChanges();
    expect(component.goHome).toHaveBeenCalled();
  }));

The test result is "Expected spy goHome to have been called." Any thoughts on how to get this to work?

1 Answer 1

2

You should be using fixture and debugElement instead of querySelector

 it('should call goHome function', async(() => {
    spyOn(component, 'goHome');
    fixture.detectChanges();
    let button = fixture.debugElement.queryAll(By.css('button')).nativeElement; // modify here
    button.click();
    fixture.detectChanges();
    expect(component.goHome).toHaveBeenCalled();
  }));
Sign up to request clarification or add additional context in comments.

5 Comments

The modified 'button' variable is returned as 'undefined'. The initial use of instance is defined as instance = fixture.debugElement.nativeElement; in the beforeEach. I'm still new at this so I'm unclear of the difference.
fyi.. When I console.log(button) using the original code it returns: <button _ngcontent-c15="" class="btn btn-primary" id="homeBtn" tabindex="0" ng-reflect-router-link="home">Home</button> The previous test using the same definition of 'button' verifies the button renders. What's confusing to is the test's failure to either call the goHome function or to recognize it's been called.
.queryAll() returns an array. The above code is not working!
He's correct about using debugElement over querySelector for platform agnostic testing. But yeah calling click on an array is not correct. Looking for the same thing, testing that elements are clickable.
.queryAll() should be .query()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.