Marco Ripamonti
Marco Ripamonti

Reputation: 247

Test component with subscription in ngOnInit - Angular 11

I'm new to testing in Jasmine and Karma and I'm trying to figure out how to test the observable of a component which gets set in the ngOnInit.

I have the following component:

export class Component implements OnInit{
    
    currentConfig$: Observable<Config>;
    
    constructor(
         private someService: someService,
         private route: ActivatedRoute
    ){}

    ngOnInit() {
        this.route.params.subscribe(params => {
            this.someService.init(params.name)
        })
        this.currentConfig = this.someService.config$
    }

}

Here's the service:

export class SomeService {

    private subject: Subject<Config> = new Subject()
    
    readonly config$ = subject.asObservable()

    init(name: string): void {
        this.findOne(name).subscribe(res => {
            if (res)
                this.subject.next({name})
        })
        
    }

    findOne(name): Observable<any> {
        return this.http.get('api');
    }

}

And then I can subscribe to the currentConfig observable with the async pipe in the template. I'm trying to test the values emitted by currentConfig$ of the Component.

Here's what my test looks like now:

fit('should show create component', fakeAsync(() => {
    const component = fixture.componentInstance;
    const debugElement = fixture.debugElement;
    const expected = {name: 'ok'}

    // this works
    someService.config$.subscribe(config => console.log(config));
    
    // this doesn't work even if I put it after the detect changes
    // component.currentConfig$.subscribe(config => console.log(config));

    spyOn(someService, 'findOne').and.returnValue(of(expected));
    fixture.detectChanges(); 

}));

The component currentConfig$ is undefined if I try to subscribe to it. Could it be because of the async pipe which consumes the value emitted? Or I'm totally in the wrong direction? And how could I test it then?

I'm also still pretty confused about the spyOn. Should spy on a function of the service that deep inside of the ngOnInit?

EDIT

I think I solved it. I had to call component.ngOnInit() instead of fixture.detectChanges(), then subscribe to the component observable and then call fixture.detectChanges().

Here's the final test working:

fit('should get room config given room name', fakeAsync(() => {
    const component = fixture.componentInstance;

    const config: Config= {
        name: 'name',
    };
    // mock service response
    spyOn(roomService, 'findOne').and.returnValue(of(response));
    // call ngOnInit instead. Detect changes keeps observable cold.
    component.ngOnInit();

    component.roomConfig$.subscribe(config => expect(config).toEqual(roomConfig));
    
    // trigger second change detection.
    fixture.detectChanges();

}));

I'm not sure why detectChanges kept the observable cold. If anyone can clear that out?

Upvotes: 3

Views: 3279

Answers (1)

Andrei
Andrei

Reputation: 12036

ngOnInit is called during first change detection. in your test you could try to call it manually component.ngOnInit() or, in case if you want to run it in a more like runtime environment - by calling the change detection itself fixture.detectChanges()

Upvotes: 1

Related Questions