1

I am trying to test a simple function in angular using karma and jasmine.

class

export class Acl {
  async caller() {
    console.log("first statement");
    const calledMe = await this.callMe().toPromise();
    return calledMe;
  }

  callMe() {
    return new Observable((observer) => observer.next({ something: "key" }));
  }
}

test file

import { Acl } from "./main";
import { fakeAsync, tick } from "@angular/core/testing";
import { Observable } from "rxjs";

describe("Hello world", () => {
  it("test the async", fakeAsync(() => {
    const t = new Acl();
    spyOn(t, "callMe").and.returnValue(
      new Observable((observer) => observer.next({ something: "key" }))
    );

    const cl = t.caller();

    console.log("a print ", cl);
    tick();
  }));
});

the output for the print statement in the test case is enter image description here

How to test such functions.

2 Answers 2

1

Your test should be made up of at least one expectation, else you aren't testing for a specific result but just that the code runs without errors.

In your case I think you could store the spy as a constant and then expect that it has been called after tick, like that:

describe("Hello world", () => {
  it("test the async", fakeAsync(() => {
    const t = new Acl();
    const spy = spyOn(t, "callMe").and.returnValue(
      new Observable((observer) => observer.next({ something: "key" }))
    );

    t.caller();
    tick();

    expect(spy).toHaveBeenCalled()
  }));
});
Sign up to request clarification or add additional context in comments.

1 Comment

if I add expectation the thing remains the same. the statements after the await do not get executed. I need to test statements after the await call the code I have added is simplified version of what i am trying.
0

The test shown in the other answer is correctly written and should work -- the expect statement should fire and pass.

If it's not passing, it could be due to this Zone.js issue. If your tests are running as native async/await (target ES2017 or higher), Zone cannot hook your await statement, so tick() will not cause it to proceed. You can test this by putting logging statements immediately before and after the await line and the tick() line:

  async caller() {
    console.log("before await");
    const calledMe = await this.callMe().toPromise();
    console.log("after await");
    return calledMe;
  }

  // ...

  it("test the async", fakeAsync(() => {
    const t = new Acl();
    const spy = spyOn(t, "callMe").and.returnValue(
      new Observable((observer) => observer.next({ something: "key" }))
    );

    t.caller();
    console.log("before tick");
    tick();
    console.log("after tick");

    expect(spy).toHaveBeenCalled()
  }));

If you run this with target: "es2015" in your tsconfig, you should see "before await", "before tick", "after await", "after tick", then your test should pass. If you run it with target: "es2017" or later, like 2018/2020/esnext, you should see "before await", "before tick", "after tick", then your test should fail, then "after await" will probably log after the test body function has totally finished executing.

As an aside, this drove me crazy for about 2 days. Users of the ng CLI get a warning when they try to go past es2015, but not if you compile your project using the AngularCompilerPlugin webpack plugin directly, though that should be fixed soon.

Comments

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.