0

When running tests in Angular2+ using Karma and Jasmine, and subscripting to observables, that subscription (as I understand) is run asynchronously, so the test should be wrapped into an async or fakeAsync.

But we don't use async not fakeAsync, the tests passes, so my question is: isn't the code into the subscribe used to be asynchronous? Why is the test passing? What is going on?

hello.component.ts

import { Component, Input, OnInit } from '@angular/core';
import {of} from 'rxjs';

@Component({
  selector: 'hello',
  template: `<h1>Hello {{name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent implements OnInit  {
  name: string;

  ngOnInit() {
    // Subscribing to sayName() to get a name
    this.sayName().subscribe(name => this.name = name);
  }

  sayName() {
    return of('Foo');
  }
}

hello.component.spec.ts

import { CommonModule } from '@angular/common';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import {HelloComponent} from './hello.component';

describe('HelloComponent', () => {
  let fixture: ComponentFixture<HelloComponent>;
  let component: HelloComponent;


  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        CommonModule
      ],
      declarations: [HelloComponent],
      providers: [
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HelloComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should have foo name', () => {
    // Running out of async/fakeAsync
    // and asserting we got the name
    expect(component.name).toEqual('Foo');
  });
});

You can also check in Stackblitz this example.

Ander2
  • 5,569
  • 2
  • 23
  • 42
  • Please put a [mcve] *in the question*. Also note that a test not failing isn't the necessarily same thing as it passing. – jonrsharpe Feb 19 '19 at 17:57
  • There is a link to a working example in Stackblitz – Ander2 Feb 19 '19 at 17:58
  • I can see that, but at 3.5k you should know that a link to an example is not actually the same as it being in the question. A link is a useful supplement, but the code here is more-or-less mandatory. – jonrsharpe Feb 19 '19 at 17:59
  • I got your point, question edited and example added. – Ander2 Feb 19 '19 at 18:04
  • 1
    `Observable.of` is synchronous, so no async or fakeAsync is necessary. Other Observables, like `httpClient.get` are async. https://stackoverflow.com/questions/47135598/observable-async-vs-sync – Alex K Feb 19 '19 at 18:23

1 Answers1

1

subscribe() is a synchronous call. In your case during this call a subscription is created, values are emitted and the observer is called. Again: All of that happens while subscribe is being executed!

When you create an Observable with e.g. interval() the values will be emitted asynchronously. Everything before that still happens synchronously.

When you use the subscribeOn() operator then even the subscription would be created asynchronously.

Contrary to promises reactive streams are not asynchronous by default. And as explained before different parts of the whole process can be independently made asynchronous.

If you want to dive deeper into that topic I shamelessly recommend my article Concurrency and Asynchronous Behavior with RxJS. It's short ;)

a better oliver
  • 26,330
  • 2
  • 58
  • 66