Подтвердить что ты не робот

Как отключить встроенные fs node.js во время тестирования?

Я хочу заглушить node.js встроенные функции, такие как fs, чтобы я фактически не делал никаких вызовов на уровне системного уровня. Единственное, что я могу сделать, это передать в fs и все другие встроенные функции в качестве аргумента ко всем моим функциям, чтобы избежать использования реальных fs. Это кажется немного глупым и создает многословную подпись функции, заполненную встроенными в качестве аргументов.

var fs = require('fs');

function findFile(path, callback) {
  _findFile(fs, path, callback);
}

function _findFile(fs, path, callback) {
  fs.readdir(path, function(err, files) {
     //Do something.
  });
}

И затем во время тестирования:

var stubFs = {
  readdir: function(path, callback) {
     callback(null, []);
  }
};

_findFile.(stubFs, testThing, testCallback);

Там лучше, чем это правильно?

4b9b3361

Ответ 1

Мне нравится использовать rewire для выполнения запросов (...)

Проверяемый модуль

модуль-a.js

var fs = require('fs')
function findFile(path, callback) {
  fs.readdir(path, function(err, files) {
     //Do something.
  })
}

Код проверки

модуль-а-test.js

var rewire = require('rewire')
var moduleA = rewire('./moduleA')
// stub out fs
var fsStub = {
  readdir: function(path, callback) {
     console.log('fs.readdir stub called')
     callback(null, [])
  }
}
moduleA.__set__('fs', fsStub)
// call moduleA which now has a fs stubbed out
moduleA()

Ответ 2

Рекомендации по перепрограммированию и другим решениям для колодцев хороши, если тестируемый модуль является тем, кто совершает вызовы fs. Тем не менее, если тестируемый модуль использует библиотеку, которая использует fs под ней, rewire и другое решение для stubbing получают волосатые довольно быстро.

Теперь есть лучшее решение: mock-fs

Модуль mock-fs позволяет Node встроенному модулю fs временно резервировать файловую систему с памятью в памяти. Это позволяет запускать тесты против набора файлов-макетов и каталогов вместо того, чтобы тащить кучу тестовых приборов.

Пример (бесстыдно снят с его readme):

var mock = require('mock-fs');

mock({
  'path/to/fake/dir': {
    'some-file.txt': 'file content here',
    'empty-dir': {/** empty directory */}
  },
  'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9]),
  'some/other/path': {/** another empty directory */}
});

Ответ 3

Альтернатива (хотя я думаю, что предложение Ноя о перемотке лучше):

Напишите обертку вокруг require именем requireStubbable или около того. Поместите это в модуль, который вы настраиваете один раз, в коде настройки теста. Так как узел кэширует результат require, всякий раз, когда вам снова требуется модуль requireStubbable, вы получаете ту же настроенную функцию. Вы можете настроить его так, чтобы любое количество модулей было заглушено, а все остальные передавались без изменений.

Любой модуль, который вы хотите поддерживать, передавая заглушки, должен использовать функцию requireStubbable вместо обычного require. Модуль rewire не имеет этого недостатка и вместо этого дает контроль над вызывающим кодом.

Добавлено 26 апреля

Я никогда не осознавал, но поскольку объект (или, точнее, ссылка на объект), возвращаемая функцией require("fs"), кэшируется, вы можете просто сделать:

const fs = require("fs")
fs.readFile = function (filename, cb) {
  cb(null, new Buffer("fake contents"));
};
// etc

Когда вы включаете этот код в любом месте, fs.readFile будет указывать на вышеуказанную функцию везде. Это работает для заглушения любого модуля, который представляет собой просто набор функций (например, большинство встроенных модулей). Случаи, для которых это не работает, если модуль возвращает единственную функцию. Для этого понадобится что-то вроде rewire.

Ответ 4

Stubs - это функции/программы, которые имитируют поведение компонентов/модулей. Штыри предоставляют консервированные ответы на вызовы функций, сделанные во время тестовых случаев.

Примером может быть запись файла без фактического выполнения.

var fs = require('fs')

var writeFileStub = sinon.stub(fs, 'writeFile', function (path, data, cb) {  
 return cb(null)
})

expect(writeFileStub).to.be.called  
writeFileStub.restore()  

Ответ 5

Вот как я об этом думаю:

То, как вы это делаете, - это очевидный первый шаг, но это отстойно, когда нужно передавать эти вещи повсюду. Вызывающие ваши функции не должны заботиться о том, что вы хотите протестировать с помощью mocks. Вы не хотите просто перезаписывать или обезвреживать глобальные модули в глобальном пространстве имен для своего теста. И нормальная модель инъекции зависимостей довольно многословна в Javascript, так как нет локальной локальности класса.

Итак, вокруг всего модуля, я сделал как (function(fs, net, http) { … })(fs, net, http);

Затем внутри модуля, если есть конструктор класса, выдает необязательные дополнительные параметры для конструктора (или возможные свойства одного параметра объекта mocks или что-то еще), и ваш тест проходит в mocks. Ваш конструктор перезаписывает реальные модули node только в локальной области модуля.

Альтернативно, если модуль имеет только статические функции; есть одна такая функция, которая инициализирует mocks, вы можете проверить, что эта функция не вызывается в вашем коде prod.

Ответ 6

Посмотрите using-stubs, особенно на require ( ) часть.

Оставьте код модуля так же, как вы бы сделали обычно, например:

//myApp.js
var fs = require('fs');

fs.readdir(path, function(err, files) {
  //Do something.
});

Затем в модуле тестирования (или в любой модульной системе тестирования) используйте using-stubs, чтобы изменить (и даже совместить или подтвердить) поведение fs:

var using = require('using-stubs');

//get a reference to require('fs')
var fs = using.require('fs');

//override behaviour of fs.readdir
using(fs)('readdir').stub(function(path, callback){

    //override fs.readdir() logic
    var err = null;
    var files = [];
    // (...)

    //mock original behaviour
    callback(err, files)
})

//then run the app normally to test it (some frameworks do this for you)
require('myApp')

Теперь ваш тест переопределит внутреннее поведение fs в myApp.js, без необходимости изменять код в любом из компонентов.

Вы также можете делать другие интересные вещи, такие как проверка того, сколько раз вызывается методов, соответствие параметров или области вызова метода, даже переопределить поведение в новых экземплярах класса, используемых внутри myApp.js.

Ответ 7

Проверьте mock-fs и fake-fs, которые уже много делают.

Ответ 8

Используйте memfs файловую систему в памяти.

Ответ 9

var fs = require('./myStubFs');

Казалось бы, большое улучшение. Независимо от того, какое решение вы найдете, вероятно, будет связано с написанием собственных функций заглушки. Идеальное решение было бы более низким, поэтому вам не нужно прикасаться ко всем файлам, которые вы хотите сделать, например. возможно, вы хотите, чтобы сторонние библиотеки также были заглушены.

Ответ 10

Вот версия, которая работает с API fs.promises:

const fsMock = sinon.mock(fs.promises);
fsMock.expects('readFile').withArgs('test.json').returns(Promise.resolve(Buffer.from('{}')));

const val = await fs.promises.readFile('test.json');

expect(val.toString()).toEqual('{}');
fsMock.verify();