简体   繁体   中英

Why one of this tests with done/await works and the other one do not?

I have this async method that I want to test:

async getGeneralStats(): Promise<GeneralStats> {
    console.log("getGeneralStats()");

    let recoveredGeneralStats = await this.storage.get(StorageKeyItems.GeneralStats);
    console.log("recoveredGeneralStats", recoveredGeneralStats);

    if (!recoveredGeneralStats) {
        console.warn("There were no general stats, creating new one")
        this.generalStats = new GeneralStats();
        await this.storage.set(StorageKeyItems.GeneralStats, this.generalStats);
    } else {
        this.generalStats = recoveredGeneralStats;
        console.log("Recovered GeneralStats : ");
        console.dir(this.generalStats)
    }

    console.log("Returning from getGeneralStats")
    return this.generalStats;
}

I want to test to things (to beging with): 1. If storage.get is called and if storage.set is called when storage.get returns null.

The first test works and it is in this way:

    it("should GET the GeneralStats from Storage Service ", ((done) => {
        // Arrange
        let spy = spyOn(storage, 'get').and.callFake((storageKey) => {
            done();
            return Promise.resolve(null);
        })

        // Act
        statisticsProvider.getGeneralStats();

        // Assert
        expect(spy).toHaveBeenCalledWith(StorageKeyItems.GeneralStats);
    }));

But when I try to run the second test in the same way It fails:

    it("should SET GeneralStats in Storage if not General Stats were retrieved ", ((done) => {
        // Arrange
        spyOn(storage, 'get').and.callFake((storageKey) => {
            console.log("spyGet was called");
            return Promise.resolve(null);
        })

        let spySet = spyOn(storage, 'set').and.callFake((storageKey, value) => {
            console.log("spySet was called");
                          done();
            return Promise.resolve(null);
        })

        // Act
        statisticsProvider.getGeneralStats();

        // Assert
            expect(spySet).toHaveBeenCalledWith(StorageKeyItems.GeneralStats,statisticsProvider.generalStats);

    }));

The fail error says : "Expected spy set to have been called with [ 'GeneralStats', undefined ] but it was never called." but the console log says other thing:

getGeneralStats()
spyGet was called
recoveredGeneralStats null
There were no general stats, creating new one
spySet was called
FAILED StatisticsProvider #getGeneralStats should SET GeneralStats in Storage if not General Stats were retrieved 
Returning from getGeneralStats
ERROR Expected spy set to have been called with [ 'GeneralStats', undefined ] but it was never called. 

I have no idea why it says it was not called when it was. I even tried to "cheat" and make a variable that changes to true when the spySet is called but it stills does not work.

    it("should SET GeneralStats in Storage if not General Stats were retrieved ", ((done) => {
        let spyWasCalled = false;
        // Arrange
        spyOn(storage, 'get').and.callFake((storageKey) => {
            console.log("spyGet was called");
            return Promise.resolve(null);
        })


        let spySet = spyOn(storage, 'set').and.callFake((storageKey, value) => {
            console.log("spySet was called");
            spyWasCalled = true;
            return Promise.resolve(null);
        })

        // Act
        statisticsProvider.getGeneralStats().then(() => {
            done();

        });

        // Assert
        expect(spyWasCalled).toBeTruthy();
    }));

If I put the expect() in a .then after I call my method the test past but why I didn't need it before?

    it("should SET GeneralStats in Storage if not General Stats were retrieved ", ((done) => {
        // Arrange
        spyOn(storage, 'get').and.callFake((storageKey) => {
            console.log("spyGet was called");
            return Promise.resolve(null);
        })

        let spySet = spyOn(storage, 'set').and.callFake((storageKey, value) => {
            console.log("spySet was called");

            return Promise.resolve(null);
        })

        // Act
        statisticsProvider.getGeneralStats().then(() => {

            // Assert
            expect(spySet).toHaveBeenCalledWith(StorageKeyItems.GeneralStats, statisticsProvider.generalStats);
            done();
        });

    }));

Both test cases give inconsistent test results. Because:

  1. expect() may be executed before the target function completed its command due to asynchronous works it has. - When the test cases run, getGeneralStats() gives a Promise and may or may not move on to run expect() which may result in failure.

  2. done() in the wrong place. The purpose of done() is to be called when the asynchronous test case has been completed. So, you should call it after expect() and when you're sure that all the asynchronous executions are finished. - in first (and second) test case, it might not run the expect() at all! Because done() may be called before expect() inside those callFakes.

To validate, add more console.log() before expect() (or after too). So you can see if the callFake closure runs before or after the expect.

What I suggest

Since the function under test has asynchronous works, you have to expect the result in async manner too.

Jasmine has documented here .

You can either use async/await , Promise , or done() . Here is the example using async/await :

it("should SET GeneralStats in Storage if not General Stats were retrieved ", (async () => {
    // Arrange
    spyOn(storage, 'get').and.callFake((storageKey) => {
        console.log("spyGet was called");
        return Promise.resolve(null);
    })

    let spySet = spyOn(storage, 'set').and.callFake((storageKey, value) => {
        console.log("spySet was called");
        return Promise.resolve(null);
    })

    // Act
    await statisticsProvider.getGeneralStats();

    // Assert
    expect(spySet).toHaveBeenCalledWith(StorageKeyItems.GeneralStats, statisticsProvider.generalStats);

}));

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