0

Using jest 27.4.5 and sinon 12.0.1, I want to test the interaction between two interval functions of which one is async.

Here's my simplified code

module.exports = class MyClass {
    one() {
        console.log("one", Date.now())
    }    
    async two() {
        await Promise.resolve()
        console.log("two", Date.now())
    }
    init() {
        setInterval(this.one, 100)
        setInterval(this.two, 150)
    }
}

And here's the test:

const sinon = require("sinon")
const MyClass = require("./index")

describe("My test suite", () => {
    test("My test", async () => {
        const sandbox = sinon.createSandbox()
        const clock = sandbox.useFakeTimers()
        const myInstance = new MyClass
        myInstance.init()
        clock.tick(500)
    })
})

Now, it seems to me that the promise inside the async interval function is always executed after all executions of the first function:

Expected output:

one 100
two 150
one 200
one 300
two 300
one 400
two 450
one 500

Actual output:

one 100
one 200
one 300
one 400
one 500
two 500
two 500
two 500

The real life use case is: Interval function one does something quite often and function two asynchronously checks and sets some configs which function one uses. In order to check whether function one actually uses the expected configs, I need those functions to be executed in the right order in the test.

In the real-life environment, I have an additional problem: Some async interval function which does database queries logs "real" Unix times instead of faked ones upon console.log(Date.now()), e.g. 1641284903075 in the test. But I couldn't reproduce that behavior in a minimal example.

Making both functions async didn't help either in real-life. The execution order in the test still seems random and not according to interval times.

Edit: In real life, the interval values are as high as half an hour. So, I have to use fake timers. Actually waiting is not an option in unit tests.

cis
  • 1,259
  • 15
  • 48
  • This might be tricky. Your async methods need to be called first, so they can process in the background. When they resolve, you need the results. Maybe how you are going about this task should be changed. Async will be able to do something when it completes (resolve/reject). Maybe the async functions should call the process that needs to happen when they complete. For instance, do not check them in a loop, but rather have the main loop simply execute and keep calling the async code. Each time an async finishes, this would then call the functions to handle the return. – Steven Scott Jan 04 '22 at 13:51
  • This would have the benefit of your code being more streamlined and less dependent on each part. As things happen (success/failure) from async functions, your code can react. The other option might be to break these apart. Again, let the async complete and put the results somewhere (database, NoSQL, Queue, etc.) and then the main loop just looks at these sources for the data. This way function two executes and gets the response, that it saves somewhere. Function one loops and reads the results from the saved space, and then can execute with the new settings. – Steven Scott Jan 04 '22 at 13:53
  • @StevenScott To be honest, I don't understand your first suggestion. Based on my requirement, there is no way, function two can be outside of an interval. It needs to be executed periodically. I'd like to stress that I'm just asking on how to test the existing code. As far as I can tell, there is no problem in the actual code. It's just that I cannot test it the way I like. If I get your "other option" right, it just describes my current code: Function two saves configs somewhere and function one accesses them. But I cannot see how that solves my problem with execution order in the unit test. – cis Jan 04 '22 at 14:08
  • The code is basically executed in a single thread. Therefore, the non-async routine has to run to completion, before you can move on. The async calls, have call backs, which does basically provide the multi-threading. The issue is you need everything somewhat async, so that they can run independent of each other. You are not doing that right now, which is why the async will not run until after the sync functions. If you reverse them the async will return at some point, but again, you are stuck in the middle of your sync function. That is why I suggested trying to change the processing. – Steven Scott Jan 05 '22 at 22:50

0 Answers0