Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

654
Views
Unit testing NodeJS stream with jest - timeout issue

Given the following NodeJS Transform Stream:

class ObjectToCSVTransform extends Transform {
  private hasSetHeaders: boolean;

  constructor() {
    super({ objectMode: true });
    this.hasSetHeaders = false;
  }

  static create(): ObjectToCSVTransform {
    return new this();
  }

  _write(
    object: Record<string, unkwnown>,
    _encoding: BufferEncoding,
    callback: TransformCallback
  ): void {
    this.push(this.generateCSV(telemetry));
    callback();
  }

  private generateCSV(object: Record<string, unkwnown>): string {
    let csv = '';

    if (!this.hasSetHeaders) {
      csv += this.createCSVHeaders(object);
      this.hasSetHeaders = true;
    }

    csv += this.createRecord(object);

    return csv;
  }

  private createCSVHeaders(object: Record<string, unkwnown>): string {
    return `${Object.keys(object)}\n`;
  }

  private createCSVRecord(object: Record<string, unkwnown>): string {
    return `${Object.values(object)}\n`;
  }
}

I implemented the following test case (using jest), to test that given a stream of plain objects of the "same type" the expected output is a valid CSV representation of them:

describe('object to csv stream', () => {
  const items: Record<string, unknown> = [
    { foo: 1, bar: 2, baz: 3 },
    { foo: 10, bar: 20, baz: 30 },
    { foo: 100, bar: 200, baz: 300 },
  ];

  it('should transform a list of items to csv', (done) => {
    const expectedCsv = 'foo,bar,baz\n1,2,3\n10,20,30\n100,200,300\n';
    let csv = '';

    Readable.from(items)
      .pipe(ObjectToCSVTransform.create())
      .on('data', (csvResult) => {
        csv += csvResult;
        console.log(csvResult); // just for debugging purposes
      })
      .on('end', () => {
        console.log('streaming ended'); // just for debugging purposes
        expect(csv).toEqual(expectedCsv);
        done();
      });
  });
});

Apparently, during the test case implementation I wanted to see how my test case failed: the stream seemed to work as it logged every expected csv result line and the 'streaming ended' message also at the end once the stream ended, but the test execution did not finish at all then. It was jest that actually finished the execution after exceeding the default timeout:

thrown: "Exceeded timeout of 5000 ms for a test. Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

What am I missing here?

Note that I am currently using the jest callback to notify when the async operation has finished.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

The problem is that the returned value of csv is not matching the expected value. The actual returned value includes a \n at the end. If you add this to your expectedCsv string, your test will pass.

The timeout error is a red herring.

If you console.log(csv.replace(/\n/g, '\\n')) in the 'end' function you will see the extra linefeed at the end of the string.

import { Readable, Transform, TransformCallback } from 'stream';

class ObjectToCSVTransform extends Transform {
  private hasSetHeaders: boolean;

  constructor() {
    super({ objectMode: true });
    this.hasSetHeaders = false;
  }

  static create(): ObjectToCSVTransform {
    return new this();
  }

  // eslint-disable-next-line no-underscore-dangle
  _write(
    object: Record<string, unknown>,
    _encoding: 'utf8',
    callback: TransformCallback,
  ): void {
    this.push(this.generateCSV(object));
    callback();
  }

  private generateCSV(object: Record<string, unknown>): string {
    let csv = '';

    if (!this.hasSetHeaders) {
      csv += this.createCSVHeaders(object);
      this.hasSetHeaders = true;
    }

    csv += this.createCSVRecord(object);

    return csv;
  }

  private createCSVHeaders(object: Record<string, unknown>): string {
    return `${Object.keys(object)}\n`;
  }

  private createCSVRecord(object: Record<string, unknown>): string {
    return `${Object.values(object)}\n`;
  }
}

describe('object to csv stream', () => {
  const items: Record<string, unknown>[] = [
    { foo: 1, bar: 2, baz: 3 },
    { foo: 10, bar: 20, baz: 30 },
    { foo: 100, bar: 200, baz: 300 },
  ];

  it('should transform a list of items to csv', (done) => {
    const expectedCsv = 'foo,bar,baz\n1,2,3\n10,20,30\n100,200,300\n';
    let csv = '';

    Readable.from(items)
      .pipe(ObjectToCSVTransform.create())
      .on('data', (csvResult) => {
        csv += csvResult;
        console.log(csvResult); // just for debugging purposes
      })
      .on('end', () => {
        console.log('streaming ended'); // just for debugging purposes
        console.log(csv.replace(/\n/g, '\\n'));
        expect(csv).toEqual(expectedCsv);
        done();
      });
  });
});
over 4 years ago · Santiago Trujillo Report

0

There is something that I was missing in my test case that wasn't handling asynchrony correctly at all and therefore was making the Exceeded timeout exception to be thrown by jest:

  • surrounding the assertion by a try-catch and having the done() callback called in case the assertion failed.
.on('end', () => {
  try {
    expect(csv).toEqual(expectedCsv);
    done();
  } catch (error) {
    done(error);
  }
});

For more info / reference, check the jest docs: https://jestjs.io/docs/asynchronous#callbacks

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!