I need to find a way to properly test a recursive, async JavaScript function by utilizing fake timers

I've been working with a basic recursive function that performs as expected when run. To thoroughly test its operation at each stage, I want to utilize Sinon's fake timers.

Unfortunately, it seems that the fake timers are only affecting the initial call of the recursive function.

I'm hoping someone might have a solution for ensuring that the fake timers are active throughout the entire process.

For example:

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function ensureCount(count, { attempt, delay }) {
  attempt = attempt || 0
  console.log('Attempt', attempt)

  if (attempt === count) {
    return
  }

  await wait(delay)
  await ensureCount(count, { attempt: attempt + 1, delay })
}

Testing method used (with guidance from this resource):

it('retries after a given delay', function() {
  const clock = sinon.useFakeTimers()
  const promise = ensureCount(2, { delay: 200 })

  clock.tick(200)
  // Add your assertions here.

  clock.tick(200)
  // Add your assertions here.

  clock.tick(200)
  // Add your assertions here.

  return promise
})

Expected console output (without fake timers):

Attempt 0
Attempt 1
Attempt 2
✔ retries after a given delay (405ms)

Actual console output (using fake timers):

Attempt 0
Attempt 1
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

The Issue:

Is there a method to make the fake timers apply to every iteration of the recursive call rather than just the first one?

Answer №1

Discovering that Sinon has a hidden gem in the form of a .tickAsync() function was a game-changer for me. (Shoutout to this helpful comment).

This revised code easily resolves the issue at hand:

it('handles retries with a delay', async () => {
  const clock = sinon.useFakeTimers()
  ensureCount(2, { delay: 200 })

  await clock.tickAsync(200)
  // Perform assertions.

  await clock.tickAsync(200)
  // Perform assertions.

  await clock.tickAsync(200)
  // Perform assertions.

  clock.restore()
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Storing and accessing a rootScope parameter and its value in Angular JS cache

Is there a way to cache this basic object ($rootScope.config.app_genres) that I set via $http for a specific amount of time? $http.get($rootScope.config.app_ws+'get/genres',{},{cache:true}).success(function(response) { $rootScope.config.app ...

Effective methods for importing components in VueJS 2.0

As a newcomer to VueJs, I have a question regarding the best practice for importing components in a Vue Template Project. I currently have some components that are used in multiple views. After downloading an admin template, I noticed that the samples alwa ...

Looking to combine cells within a table using the Exceljs library

Encountered an issue while generating a worksheet in the EXCELJS library. function save_export(){ var workbook = new ExcelJS.Workbook(); var worksheet = workbook.addWorksheet('sheet', { pageSetup:{paperSize: 9, orientation:' ...

When utilizing useEffect in Next.js, an error may occur stating that the window is

It seems like there is a challenge with executing script code server side in next.js 13 when it needs to be executed client side. Currently, I am trying to implement the bulma calendar found at When importing the required library: import PasswordStrengthB ...

Ensure that the page has completely loaded using WebdriverJS

Is there a reliable method to ensure that a page has fully loaded using selenium-webdriver in JavaScript? I came across this similar query, but I require an implementation specifically in JavaScript. var webdriver = require('selenium-webdriver') ...

Utilizing Javascript or XUL windows without the use of iframes offer

I'm in the process of creating a multitab website for my bookmarks, but I've run into some issues. Here is the JavaScript version of what I'm trying to achieve: Unfortunately, there are obstacles with this method. The websites in the tabs ...

Can you confirm if this is the most efficient method for loading google-analytics and jQuery?

It's not necessary for jQuery to be loaded immediately on page load: Here is what I currently have: <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', '...']); _gaq.pus ...

Troubleshooting a Tiny Bottom Sheet Problem in react-native

On my page, I have a bottom sheet that takes up 3/4 of the space. Then, within that bottom sheet, I open another bottom sheet that only occupies 1/4 of the space (without closing the first one). ...

What is the best way to set up Flow type checking for functions passed as props in a React and Redux application?

In my app, I've been passing Redux action creators as props and want to improve type checking. Using the generic Function type has limitations, so I tried using the existential operator (*) in my Props type with no success in getting Flow to infer the ...

Applying a CSS class (or style) dynamically depending on the variable with the help of a directive

I'm facing a situation where I need to apply ng-style (or ng-class) multiple times depending on a variable. However, this repetitive task of writing ng-class for the same functionality for each widget is quite cumbersome for me. Is there a way to si ...

I was confused about the distinction between the https.get() and https.request() functions in the Node.js npm package for https

// # Exciting Nodejs Programs! const https = require('https'); https.get('https://www.google.com/', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on ...

The Angular Material dialog fails to display content when triggered within an event listener in Google Maps

Within my project, I am utilizing Angular 6.0.6 and Angular Material 6.3.0. One issue I have encountered is with a dialog component that I have added to the entryComponents in the app module. Strangely, when I attempt to open this dialog within the rightcl ...

Node.js and react-create-app are not compatible with each other

I am currently using node.js version 14.6.0 and node-v version 7.20.0 To replicate the issue, follow these steps: npx create-react-app my-app2 Once everything is installed, run npm i After completing the above steps, you may encounter the following warn ...

The Ajax validation form mistakenly redirects the echoes to a different page instead of the intended page for displaying the output

I am currently working on creating an ajax-form to validate the client-side server of my sign-up form. My goal is to have error messages displayed on the same page where they are triggered, without loading a separate page. Below is the code from my (sign ...

What is the best way to stay on track with internal anchor links when navigating with Aurelia?

I'm currently working on developing a style guide for a project and one of the features I would like to implement is a basic click behavior on anchor links, allowing them to smoothly scroll to the corresponding section on the page. For instance: < ...

The $http service encounters an error while attempting to retrieve a JSON file from the server

When attempting to fetch a file from the server, I utilize the $http service in the following manner: $http({url: url, method: "GET", withCredentials: true}); For some mysterious reason, an exception is only thrown when trying to retrieve JSON files from ...

Looking to set an object value as optional in JavaScript?

Hello, I am currently in the process of developing a web application using ReactJS with Auth0 for user authentication. In order to update user information on my backend, I am utilizing state to store the necessary data. My challenge lies in allowing eith ...

Providing static content within the generated code structure by Yeoman for the Angular Fullstack framework

I'm sticking to the code structure generated by Yeoman for an Angular fullstack application. The issue I'm facing is how to include a script called core.js in a file named app.html. <script src="core.js"></script> I can't fi ...

HighStock chart malfunctioning with inaccurate epoch datetime display

I am working on a project that involves creating a dynamic Highstock chart to showcase the daily influx of emails. The data is stored in a JSON file that gets updated every day, and you can see a snippet of it below: [{ "name": "Month", "data": [147199320 ...

Getting duplicate tokens for multiple users while utilizing Firebase messaging

When attempting to acquire a token from firebase, I employ the code snippet provided below: const messaging = firebase.messaging(); messaging.requestPermission() .then(() =>{ return firebase.messaging().getToken(); }).then(token => { s ...