如何测试 fs.read 函数回调

How to test the fs.read function callback

我想测试下面的代码。

class Tailer {

  constructor(logFilePath) {
    this.logFilePath = logFilePath;
    this.seekFrom = 0;
    this.lastLines = [];
  }

  setupFile(newlinepointer) {
    var bytesToRead = this.fileSizeInBytes - newlinepointer
    fs.open(this.logFilePath, 'r', (errOpen, fd) => {
      fs.read(fd, Buffer.alloc(bytesToRead), 0, bytesToRead, newlinepointer, (errRead, bytesRead, buffer) => {
        var data = buffer.toString('utf8')
        this.lastLines = data.split('\n')        
      });
    });
  }
}

我的测试代码是

describe("tailer", function() {
  describe("setupFile", function() {
    it("should fill last 10 lines from given pointer", function() {
      let tailer = new Tailer('test/test.log', (data) => {})
      tailer.setupFile(5)
      assert.equal(tailer.lastLines.length, 10);
    });
  });
});

由于 fs.read 正在使用回调,我无法测试 setupFile 函数,因为调用 setupFilelastLines 字段尚未填充。我正在使用 mocha 进行测试。

您应该使用 sinonjs 存根库来存根 fs.open()fs.read() 方法。为他们提供伪造的实现。然后你可以在你的测试用例中手动执行回调。

例如

index.js:

import fs from 'fs';

export class Tailer {
  logFilePath;
  seekFrom;
  lastLines;
  fileSizeInBytes;

  constructor(logFilePath) {
    this.logFilePath = logFilePath;
    this.seekFrom = 0;
    this.lastLines = [];
    this.fileSizeInBytes = 100;
  }

  setupFile(newlinepointer) {
    var bytesToRead = this.fileSizeInBytes - newlinepointer;
    fs.open(this.logFilePath, 'r', (errOpen, fd) => {
      fs.read(fd, Buffer.alloc(bytesToRead), 0, bytesToRead, newlinepointer, (errRead, bytesRead, buffer) => {
        var data = buffer.toString('utf8');
        this.lastLines = data.split('\n');
      });
    });
  }
}

index.test.js:

import { Tailer } from './';
import fs from 'fs';
import sinon from 'sinon';
import { expect } from 'chai';

describe('66332328', () => {
  it('should pass', () => {
    const openStub = sinon.stub(fs, 'open').callsFake((filepath, flags, callback) => {
      callback(null, 1);
    });
    const readStub = sinon.stub(fs, 'read').callsFake((fd, buffer, offset, length, position, callback) => {
      callback && callback(null, 100, Buffer.from('teresa teng\nbest singer'));
    });

    const tailer = new Tailer('./test.text');
    tailer.setupFile(50);
    expect(tailer.lastLines).to.be.deep.equal(['teresa teng', 'best singer']);
    sinon.assert.calledWithExactly(openStub, './test.text', 'r', sinon.match.func);
    sinon.assert.calledWithExactly(readStub, 1, Buffer.alloc(50), 0, 50, 50, sinon.match.func);
  });
});

单元测试结果:

  66332328
    ✓ should pass


  1 passing (6ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.ts |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------

源代码:https://github.com/mrdulin/expressjs-research/tree/master/src/Whosebug/66332328