【问题标题】:How to mock file I/O in sinon?如何在 sinon 中模拟文件 I/O?
【发布时间】:2021-12-05 13:01:58
【问题描述】:

我有一个从目录解压缩文件的功能。它工作正常

index.js

const unZip = async (zipFilePath, destDir) => {
  await util.promisify(fs.mkdir)(destDir);

  return new Promise((resolve, reject) => {
    fs.createReadStream(zipFilePath)
      .pipe(unzipper.Extract({ path: destDir }))
      .on("close", () => resolve(destDir))
      .on("error", (err) => {
        console.log("Error inside unzip", err);
        reject(err);
      });
  });
};

但是对于单元测试,我使用sinonava,我无法通过测试用例 这是代码

index.test.js

ava.beforeEach(() => {
  // mockFs({
  //   'fakeDir/fakeFile': mockFs.load('test/helpers/file/testFile.txt'),
  //   fakeFileContent: 'content here',
  // });
  sinon.stub(mockFs, 'createReadStream').returns({
    pipe: sinon.stub().returns({
      on: sinon.stub().returns({
        on: sinon.stub().returns(),
      }),
    }),
  });
});

ava.serial('unZip test', async (t) => {
  const unzip = proxyquire('../../../src/helpers/file/unZip', {
    fs: mockFs,
    util: {},
    unzipper: { Extract: () => Buffer.from([8, 6, 7, 5, 3, 0, 9]) },
  });

  const result = await unzip('fakeFileContent', 'fakeFileContent');

  t.is(result, true);
});

它给了我这样的错误

  unZip test

  Rejected promise returned by test. Reason:

  Error {
    code: 'EEXIST',
    errno: -17,
    path: 'fakeFileContent',
    syscall: 'mkdir',
    message: 'EEXIST: file already exists, mkdir \'fakeFileContent\'',
  }

【问题讨论】:

    标签: javascript node.js unit-testing sinon ava


    【解决方案1】:

    试试这个

    index.test.js

    const mockFs = {
      createReadStream: function () {
        return this;
      },
      mkdir: function (p, cb) {
        cb(null, this);
      },
      pipe: function () {
        return this;
      },
      on: function (param, cb) {
        if (param === 'close') {
          return cb();
        }
        if (param === 'error') {
          return this;
        }
      },
    };
    
    ava.serial('unZip success', async (t) => {
      const unzip = proxyquire('../../../src/helpers/file/unZip', {
        fs: mockFs,
        util: {},
        unzipper: { Extract: () => Buffer.from([8, 6, 7, 5, 3, 0, 9]) },
      });
      const mockZipFilePath = '../file/testFile.txt';
      const destinationFilePath = '../file';
    
      const result = await unzip(mockZipFilePath, destinationFilePath);
    
      t.is(result, destinationFilePath);
    });
    

    【讨论】:

      【解决方案2】:

      你不需要使用proxyquire 包,使用sinon.stub(obj, 'method') 来存根对象的方法。您可以存根 fs.mkdirunzipper.Extractfs.createReadStream 方法。

      你使用util.promisifyfs.mkdir转换为promise形式并调用它,但底层仍然是回调被调用,所以你需要使用.callsFake()方法模拟fs.mkdir的实现,并调用在测试用例中手动回调。

      下面的例子使用mocha作为测试框架,但ava也应该没问题。

      index.js:

      const fs = require('fs');
      const util = require('util');
      const unzipper = require('unzipper');
      
      const unZip = async (zipFilePath, destDir) => {
        await util.promisify(fs.mkdir)(destDir);
      
        return new Promise((resolve, reject) => {
          fs.createReadStream(zipFilePath)
            .pipe(unzipper.Extract({ path: destDir }))
            .on('close', () => resolve(destDir))
            .on('error', (err) => {
              console.log('Error inside unzip', err);
              reject(err);
            });
        });
      };
      
      module.exports = unZip;
      

      index.test.js:

      const unZip = require('./');
      const fs = require('fs');
      const sinon = require('sinon');
      const unzipper = require('unzipper');
      
      describe('69616649', () => {
        afterEach(() => {
          sinon.restore();
        });
        it('should pass', async () => {
          sinon.stub(fs, 'mkdir').callsFake((path, callback) => {
            callback();
          });
          const rs = {
            pipe: sinon.stub().returnsThis(),
            on: sinon.stub().callsFake(function (event, callback) {
              if (event === 'close') {
                callback();
              }
            }),
          };
          sinon.stub(fs, 'createReadStream').returns(rs);
          sinon.stub(unzipper, 'Extract');
          const actual = await unZip('fakeFileContent', 'fakeFileContent');
          sinon.assert.match(actual, 'fakeFileContent');
          sinon.assert.calledWithExactly(fs.mkdir, 'fakeFileContent', sinon.match.func);
          sinon.assert.calledWithExactly(fs.createReadStream, 'fakeFileContent');
          sinon.assert.calledWithExactly(unzipper.Extract, { path: 'fakeFileContent' });
          sinon.assert.calledOnce(rs.pipe);
          sinon.assert.calledWithExactly(rs.on, 'close', sinon.match.func);
        });
      });
      

      测试结果:

        69616649
          ✓ should pass
      
      
        1 passing (7ms)
      
      ----------|---------|----------|---------|---------|-------------------
      File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
      ----------|---------|----------|---------|---------|-------------------
      All files |   81.82 |      100 |      75 |   81.82 |                   
       index.js |   81.82 |      100 |      75 |   81.82 | 13-14             
      ----------|---------|----------|---------|---------|-------------------
      

      【讨论】:

      • 感谢您的回复,但是您的代码不起作用,我使用 ava 进行了修改,上面写着 Test finished without running any assertions @slideshowp2
      • @NinjaDeveloper 你把it改成ava.serial了吗?
      • 是的,我已经在答案@slideshowp2 中发布了模拟
      【解决方案3】:

      我是 ava 的新手,所以可能是错的

      proxyquire('../../../src/helpers/file/unZip')// actual function file
      
      ava('69616649', () => {
        ava.afterEach(() => {
          sinon.restore();
        });
        ava.serial('should pass', async () => {
          sinon.stub(fs, 'mkdir').callsFake((path, callback) => {
            callback();
          });
          const rs = {
            pipe: sinon.stub().returnsThis(),
            on: sinon.stub().callsFake(function (event, callback) {
              if (event === 'close') {
                callback();
              }
            }),
          };
          sinon.stub(fs, 'createReadStream').returns(rs);
          sinon.stub(unzipper, 'Extract');
          const actual = await unZip('fakeFileContent', 'fakeFileContent');
          sinon.assert.match(actual, 'fakeFileContent');
          sinon.assert.calledWithExactly(
            fs.mkdir,
            'fakeFileContent',
            sinon.match.func
          );
          sinon.assert.calledWithExactly(fs.createReadStream, 'fakeFileContent');
          sinon.assert.calledWithExactly(unzipper.Extract, {
            path: 'fakeFileContent',
          });
          sinon.assert.calledOnce(rs.pipe);
          sinon.assert.calledWithExactly(rs.on, 'close', sinon.match.func);
        });
      });
      

      【讨论】:

        猜你喜欢
        • 2011-12-15
        • 1970-01-01
        • 1970-01-01
        • 2010-10-04
        • 2015-06-08
        • 2012-05-01
        • 2010-12-25
        • 2013-12-18
        • 2010-11-06
        相关资源
        最近更新 更多