Dado el siguiente flujo de transformación de NodeJS:
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`; } }Implementé el siguiente caso de prueba (usando jest ), para probar que dada una secuencia de objetos simples del "mismo tipo" , el resultado esperado es una representación CSV válida de ellos:
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(); }); }); }); Aparentemente, durante la implementación del caso de prueba, quería ver cómo fallaba mi caso de prueba : la transmisión parecía funcionar, ya que registraba todas las líneas de resultados csv esperadas y el mensaje 'streaming ended' también al final una vez que finalizó la transmisión, pero la ejecución de la prueba no terminó en absoluto entonces. Fue una broma que en realidad terminó la ejecución después de exceder el tiempo de espera predeterminado:
throw: "Se excedió el tiempo de espera de 5000 ms para una prueba. Use jest.setTimeout(newTimeout) para aumentar el valor de tiempo de espera, si se trata de una prueba de larga duración".
¿Que me estoy perdiendo aqui?
Tenga en cuenta que actualmente estoy usando la devolución de llamada de broma para notificar cuando finaliza la operación asíncrona.
El problema es que el valor devuelto de csv no coincide con el valor esperado. El valor devuelto real incluye un \n al final. Si agrega esto a su cadena expectedCsv , su prueba pasará.
El error de tiempo de espera es una pista falsa.
Si console.log(csv.replace(/\n/g, '\\n')) en la función 'fin', verá el avance de línea adicional al final de la cadena.
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(); }); }); });Hay algo que me faltaba en mi caso de prueba que no estaba manejando la asincronía correctamente y, por lo tanto, estaba haciendo que la Exceeded timeout exception de espera excedido fuera lanzada por broma:
done() en caso de que la afirmación falle. .on('end', () => { try { expect(csv).toEqual(expectedCsv); done(); } catch (error) { done(error); } });Para obtener más información/referencia, consulte los documentos de jest: https://jestjs.io/docs/asynchronous#callbacks