Running a Jest test that triggers process.exit within an eternal setInterval loop with a latency of 0, all while utilizing Single

In the original project, there is a class that performs long-running tasks in separate processes on servers. These processes occasionally receive 'SIGINT' signals to stop, and I need to persist the state when this happens. To achieve this, I wrapped the work in an 'setInterval(,0)' function to allow the event loop to capture interrupts, as it wouldn't work within a 'while(true)' loop.

Now, I want to test if the Subject class works correctly - specifically by validating input. You can find a Minimal Working Example (MWE) here.

The work is implemented in 'subject.class.ts':

import { writeFileSync } from "fs";
import { tmpdir } from "os";
import { join, basename } from "path";
export default class Subject {
  private counter = 0;
  public doWork(inputdata: string): void {
    if (!inputdata) {
      process.exit(100); // exit if no input was given
    }
    setInterval(() => {
      this.counter++;
      if (this.counter > 10e2) {
        process.exit(0);
      }
      if (this.counter % 10e1 === 0) {
        console.log(this.counter);
      }
    }, 0);
  }
  public persist(): void {
    const data = JSON.stringify(this.counter);
    const path = join(tmpdir(), basename(__filename));
    writeFileSync(path, data);
    console.log(`Persisted to ${path}`);
  }
}

To start the work, I create a new Subject instance, set interrupt handlers, and call the 'subj.doWork("peanut")' method in 'main.ts':

import Subject from "./subject.class";

const subj = new Subject();

process.on("exit", (exitcode: number) => {
  if (exitcode === 0) {
    process.stdout.write(`\nDone Success. :)\n`);
  } else {
    process.stderr.write(`\nDone with code: ${exitcode}\n`);
  }
  subj.persist();
  process.stdout.write("exiting.");
  process.exit(exitcode);
});

process.on("SIGINT", (signal: "SIGINT") => {
  process.stdout.write(`\ncaught ${signal}`);
  process.exit(13);
});

subj.doWork("peanut");

Everything functions correctly. However, when testing the call to 'process.exit', Jest exits before 'done' is called in the first 'it' block. Jest then displays error messages about attempted log writes. When unskipping the second case, the first one passes because Jest is still active. But the 'expect.toEqual(0);' statement is never executed, causing the test to succeed regardless of the value used. How can I properly test the Subject's work method?

Answer №1

Here are a few key points to consider:

  1. Since process.exit() is being mocked and no longer exits, make sure to clear the interval and return when process.exit() is called.
  2. When writing Jest tests, it's important to fake the Timer. Refer to the Jest Documentation on Timer-Mocks for more information.
  3. Depending on your test setup (such as using singleton patterns in the real project), it can be helpful to have one test file per test/it statement to prevent collisions with singleton instances. Each test file runs on its own thread and may run in parallel.
  4. Consider using jest --runInBand for running tests sequentially.

subject.class.ts:


if (!inputdata) {
  return process.exit(100); // remember to 'return'
}
const id = setInterval(()=> // save interval-id
  this.counter++;
  if (this.counter > 10e2) {
    clearInterval(id);      // clear before exiting
    return process.exit(0); // remember to 'return'
  }
  if (this.counter % 10e1 == 0) {
    console.log(this.counter);
  }
}, 0);

tests/subject.test.ts:


beforeAll(() => {
    jest.useFakeTimers();
});
afterAll(() => {
    jest.useRealTimers();
});


// In the test, make sure to run the Timers
expect(() => new Subject().doWork("")).not.toThrow();
jest.runAllTimers();

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

Steps for transitioning from a mapped type to a discriminated union

Forgive me if this question has already been posed. I made an effort to search for a solution, but it seems I may not be using the correct terms. The issue arises with this particular structure. It involves a simple mapped type: type Mapped = { squ ...

Learn the method to conceal rows within a table simply by toggling a button

I need a function that will hide the rows below a row with a header and button, and only reveal them when another row with a header and button is clicked. When one of the +/- buttons is clicked, it should hide or expand all the rows with data content. http ...

Adding JSON data to an array for Flot Diagram

I've been struggling with the task of organizing data into an array. The existing temp array is structured as follows: tmp[0][0] = "NY" tmp[0][1] = "52" tmp[1][0] = "FL" tmp[1][1] = "25" My goal is to transfer this data into a new array called data. ...

The element type is not valid: it should be a string for built-in components or a class/function for composite components, but it is currently an object in a React project

In the process of developing a React app to explore MUI capabilities, I encountered an error in my browser: The issue reported is: Element type is invalid - expected a string (for built-in components) or a class/function (for composite components), but rec ...

Maximizing jQuery DataTables performance with single column filtering options and a vast amount of data rows

My current project involves a table with unique column select drop-downs provided by an amazing jQuery plug-in. The performance is excellent with about 1000 rows. However, the client just informed me that the data could increase to 40000 rows within a mont ...

Is there a way to deactivate the Edge mini menu while selecting text in a React application?

I have recently been working on a React web application and managed to create a specialized text selection menu. However, I am facing a challenge in programmatically disabling the default text selection mini menu in React. The image attached illustrates bo ...

Using parameters and data type in Typescript

When I remove <IFirst extends {}, ISecond extends {}> from the declaration of this function, the compiler generates an error. Isn't the return value supposed to be the type after the double dot? What does <IFirst extends {}, ISecond extends { ...

Using Javascript to dynamically copy text to the clipboard

Is there a way to create a function that can automatically copy the current URL in the address bar to the clipboard? Before jumping to conclusions, hear me out: I'm looking for a solution where the copying process happens dynamically, not just when ...

Establishing a minimum width for the value of zero in AmCharts column series by customizing the bullet labels

Here's an interesting example where I have arranged column series with bullet labels. However, you may notice that there are some 0 values present as well. My goal is to establish a minimum width for the series so that even if the value is small or 0, ...

Can someone please help me convert this jQuery code into vanilla JavaScript?

My Cordova app has an email sending function that utilizes jQuery. During debugging, the ajax function works perfectly in my browser, but once I build the app and test it on my phone, it stops working. I encountered a similar issue before, which was resolv ...

Tips on invoking a scope function depending on an attribute's value

In my application, there are multiple 'save and close' links, each with a unique function triggered when clicked, specified by the directive ng-really-click. This directive confirms closure before executing the designated function. For example: ...

Cheerio's method of extracting data from an HTML table using web scraping

Currently, I am facing an issue with a web scraping project. The specific webpage that I am attempting to scrape looks something like this: <table style="position..."> <thead>..</thead> <tbody id="leaderboard_body"> ...

Querying the api for data using Angular when paginating the table

Currently, I have a table that retrieves data from an API URL, and the data is paginated by default on the server. My goal is to fetch new data when clicking on pages 2, 3, etc., returning the corresponding page's data from the server. I am using an ...

Ways to showcase a standalone identifier from iTunes RSS

I have a script below that fetches iTunes charts directly from the RSS and displays it. However, I am looking to only display the information for a specific ID from the RSS entry. Any suggestions on how this can be achieved? <script> jQuery(functi ...

What causes the delay in loading certain CSS and JS files?

My ASP.NET MVC5 application hosted on an IIS Server has a slow-loading login page, especially after clearing the browser history. The Developer Tools Network field indicates a problem with loading page contents such as css and js files. Can you provide gui ...

"Enhance your software with a customizable interface or develop new functionalities to generate analogous

Having API data with a similar structure, I am looking to streamline my code by filtering it through a function. However, as someone new to TypeScript, I am struggling to implement this correctly using a function and an interface. Essentially, I aim to ach ...

What is the purpose of the Express 4 namespace parameter?

Having worked extensively with Express 4, I recently attempted to implement a namespaced route with a parameter. This would involve routes like: /:username/shows /:username/shows/:showname/episodes I figured this scenario was ideal for express namespacin ...

The load event in React's iframe is failing to fire after the src attribute is modified using state

In the process of creating a registration form for a React application, we encountered the need to incorporate an HTML legal agreement as an iframe. This legal document is available in various languages, one of which can be selected using a drop-down menu; ...

Guide to making a slider menu using html, css, and javascript

Just dipping my toes into the world of web development. I'm intrigued by the idea of creating a "slider menu" where users can view and select options by clicking on next or previous buttons (see example image below). While I've got some basic HTM ...

The map displayed on google.com appears different from the one featured on our website

The integration of the JS for the Google map on our website is working smoothly without any issues. However, when I zoom into our address on google.com/maps, our Hotel is listed as "Hotel". On the map displayed on our website, there are only a few entries ...