Sinon
async testing
JavaScript
test automation
array manipulation

Sinon async test array is not filled in before push happens

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

When an async test says an array was not filled before push happened, the usual problem is not Sinon itself. The usual problem is that the test made an assertion before the asynchronous work finished. In JavaScript tests, ordering is controlled by await, returned promises, callbacks, or fake timers. If the test does not wait correctly, the array state will look incomplete.

The Real Bug Is Usually Missing Synchronization

Consider this pattern:

javascript
1async function fillThenPush(items, fetchValue) {
2  const value = await fetchValue();
3  items.push(value);
4}

A broken test often looks like this:

javascript
1it("fills the array", function () {
2  const items = [];
3  const stub = sinon.stub().resolves("A");
4
5  fillThenPush(items, stub);
6
7  expect(items).to.deep.equal(["A"]); // too early
8});

The assertion runs before the promise settles, so the array is still empty.

Fix It by Awaiting the Async Work

The clean fix is to make the test async and await the function under test.

javascript
1it("fills the array", async function () {
2  const items = [];
3  const stub = sinon.stub().resolves("A");
4
5  await fillThenPush(items, stub);
6
7  expect(items).to.deep.equal(["A"]);
8  sinon.assert.calledOnce(stub);
9});

Now the test waits until the asynchronous push has actually happened.

Return the Promise If You Do Not Use async

Mocha and similar test runners also understand returned promises.

javascript
1it("fills the array", function () {
2  const items = [];
3  const stub = sinon.stub().resolves("A");
4
5  return fillThenPush(items, stub).then(() => {
6    expect(items).to.deep.equal(["A"]);
7  });
8});

This is equivalent in intent: the test does not finish until the returned promise resolves.

Fake Timers Help Only for Timer-Based Async

If the async ordering depends on setTimeout, setInterval, or similar timer APIs, Sinon fake timers can help drive the scheduled work.

javascript
1it("pushes after timeout", async function () {
2  const clock = sinon.useFakeTimers();
3  const items = [];
4
5  function pushLater() {
6    return new Promise((resolve) => {
7      setTimeout(() => {
8        items.push("A");
9        resolve();
10      }, 1000);
11    });
12  }
13
14  const promise = pushLater();
15  await clock.tickAsync(1000);
16  await promise;
17
18  expect(items).to.deep.equal(["A"]);
19  clock.restore();
20});

Fake timers do not automatically solve promise ordering by themselves. They only advance timer-based scheduling.

Stub the Right Boundary

Sometimes the array is updated in a callback chain rather than directly after a promise. In those cases, stub the asynchronous dependency at the right level so the function under test still runs its own real logic.

If you stub too much, you may end up testing the stub rather than the array mutation path. If you stub too little, your test becomes flaky because it depends on timing you do not control.

If the code under test chains several promises, await the outermost promise, not an internal helper you happen to know about. Tests should synchronize with public behavior, not with fragile implementation timing.

Common Pitfalls

The biggest mistake is writing a synchronous assertion for asynchronous work. If the function returns a promise, the test must await it or return it.

Another issue is using fake timers for promise-only code that does not actually depend on timers. Advancing the clock will not resolve an unrelated unresolved promise.

People also often forget to restore fake timers, which can leak altered timing behavior into later tests and create very confusing failures.

Finally, do not mix callback-style completion and returned promises carelessly in the same test. Pick one synchronization style and use it consistently.

Summary

  • If the array is empty too early, the test is usually asserting before the async work completes.
  • Use await or return the promise from the test.
  • Use Sinon fake timers only when the async behavior depends on timer APIs.
  • Stub the dependency boundary carefully so the real mutation path still executes.
  • Async test failures about ordering are usually synchronization bugs, not array bugs.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.