简体   繁体   中英

Test asynchronous code in Jasmine

I am using Jasmine to write some test cases. It looks like this:

Class Definition:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  getFullName() {
    return this.firstName + this.lastName;
  }

  getName () {
    setTimeout(() => {
      return this.getFullName();
    }, 100)
  }
}

module.exports = Person;

Test code:

 const Person = require('./index');

 describe('Test Person methods', () => {
  beforeEach(() => {
    programmer = new Person('John', 'Gray');
    spyOn(programmer, 'getFullName');
    programmer.getName();
  });

  it('getName should call getFullName', () => {
    expect(programmer.getFullName).toHaveBeenCalled();
  })
});

getName is an async function which calls getFullName after some time. Because of this async nature, the it spec executes immediately after getName is called, and so the test fails.

I know that I can use done function of Jasmine to carry out async testing, but I am not sure how to implement it here? In the docs, the done method is called from within the async code only, but that code is not accessible to the test as its handled internally by getName method.

Or is there any other approach which does not use done method?

Here is a working sample of this code.

I think you need to pass the done parameter to the function and call it like so:

beforeEach((done) => {
    someObj = new SomeClass();
    spyOn(someObj, 'onSuccess');
    someObj.start();
    done();
});

it('start should call onSuccess', (done) => {
    expect(someObj.onSuccess).toHaveBeenCalled();
    done();
});

Source: Jasmine Documentation

Your implementation of getName is wrong. In javascript, to handle results from asynchronous functions you either need to pass a callback to your async function or return a Promise . Once you fix your implementation, then testing it should be straightforward:

Using a callback approach:

getName (callback) {
    setTimeout(() => {
        callback(this.getFullName());
    }, 100)
}

beforeEach(done => {
    ...
    programmer.getName(name => { 
      done(); // at this point the async func. has completed
    });
});

Using a Promise:

getName () {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(this.getFullName());
        }, 100)
    }) 
}

beforeEach(done => {
    ...
    programmer
        .getName()
        .then(name => {
            done(); // at this point the async func. has completed
        });
});

EDIT:

If you're using a Promise you can use async / await as well (Jasmine >= 2.7):

beforeEach(async () => {
    ...
    const name = await programmer.getName();
    // async func has completed here. 
    // await acts like .then() and "waits" for your promise to resolve
    // done wouldn't be needed because there aren't any callbacks
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM