angularjasminengoninit

A method inside ngOnInit is not called during testing


I'm trying to test if a service method is called from ngOnInit. However, I get a strange behavior: the method is not called although ngOnInit is executed.

import { Component } from '@angular/core';
import { SampleService } from './sample.service';

@Component({
  selector: 'app-sample',
  templateUrl: './sample.component.html',
  styleUrls: ['./sample.component.css']
})
export class SampleComponent {
  todos:any;

  constructor(private service: SampleService){}

  ngOnInit(){
    this.todos = 'A';
    this.service.getAll().subscribe(
      res => {
        this.todos = res;
      }
    );
  }
}
describe('SampleComponent', () => {
  let component: SampleComponent;
  let fixture: ComponentFixture<SampleComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [ HttpClientModule ],
      declarations: [ SampleComponent ],
      providers: [ SampleService ]
    })
    .compileComponents();

    fixture = TestBed.createComponent(SampleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should load todos from the server', () => {
    let todos = [1,2,3];
    let service = TestBed.inject(SampleService);
    spyOn(service,'getAll').and.returnValue(of(todos));
    
    fixture.detectChanges();
    //component.ngOnInit();
    
    expect(component.todos).toEqual(todos);
  });
  
});

If I test this code, the test fails and it return the message "Expected 'A' to equal [ 1, 2, 3 ]". So 'ngOnInit' is executed but the method 'getAll' is not called.

However, if I uncomment 'component.ngOnInit()', so I call ngOnInit manually, the test works perfectly (it returns a SUCCESS message).

Do you understand why this happens? Why getAll is not called?

More information:

I tried to edit my test to make it easier to understand the issue. Now I use toHaveBeenCalledWith() to test if the method is called. And, like I supposed, it is not called at all, even though ngOnInit is executed. With the following test I get the message: Expected spy getAll to have been called once. It was called 0 times. However, uncommenting component.ngOnInit(), the method is called.

  it('should load todos from the server', () => {
    let todos = [1,2,3];
    let service = TestBed.inject(SampleService);
    let spy = spyOn(service,'getAll').and.returnValue(of(todos));

    fixture.detectChanges();
    //component.ngOnInit();

    expect(spy).toHaveBeenCalledTimes(1);
  });

Solution

  • I found a solution, so I decided to answer my own question.

    I just copied the definition of fixture and component instance in my it() method. So I redefine them before calling spyOn and before executing fixture.detectChanges(). Doing so, it works.

    it('should load todos from the server', () => {
      fixture = TestBed.createComponent(SampleComponent);
      component = fixture.componentInstance;
      let todos = [1,2,3];
      let service = TestBed.inject(SampleService);
      spyOn(service,'getAll').and.returnValue(of(todos));
    
      fixture.detectChanges();
    
      expect(component.todos).toEqual(todos);
    });
    

    I don't know whether this is a good practice or not, but for now it seems the only way to test ngOnInit as integration test.