Jest.js 如何对非导出函数进行单元测试?

f8rj6qna  于 2023-01-18  发布在  Jest
关注(0)|答案(6)|浏览(342)

在一个JavaScript ES6模块中,可能有许多小的、易于测试的函数需要测试,但不应该导出。如何在不导出的情况下测试模块中的函数?(不使用Rewire)

c9x0cxw0

c9x0cxw01#

导出“exportedForTesting”常量

function shouldntBeExportedFn(){
  // Does stuff that needs to be tested
  // but is not for use outside of this package
}

export function exportedFn(){
  // A function that should be called
  // from code outside of this package and
  // uses other functions in this package
}

export const exportedForTesting = {
  shouldntBeExportedFn
}

以下代码可用于生产代码:

import { exportedFn } from './myPackage';

这可以用在单元测试中:

import { exportedFn, exportedForTesting } from './myPackage';
const { shouldntBeExportedFn } = exportedForTesting;

这个策略为我的团队中的其他开发人员保留了上下文线索,即除了测试之外,不应该在包之外使用shouldntBeExportedFn()
我已经用了很多年了,我发现它非常有效。

am46iovg

am46iovg2#

我希望我能给你一个更好的答案,乔丹。😊过去我在JavaScript和C#上下文中都有过非常相似的问题...

回答/不回答

在某种程度上,我不得不接受这样一个事实:如果我想要覆盖未导出/私有函数/方法的粒度单元测试,我真的应该公开它们。有些人会说这违反了封装,但也有人不同意。前一组人还会说,在函数被导出/公开之前,它本质上是一个实现细节,因此不应该进行单元测试。
如果你正在练习TDD,那么Mark Seeman's explanation should be relevant(Pluralsight),希望它能澄清为什么可以暴露东西。
我不知道您是否可以找到一些技巧,直接从您的单元测试中调用未导出的函数,而不对测试中的代码进行更改,但我个人不会这样做。

只是一个选择

另一种选择是将库一分为二,比如,库A是应用程序代码,库B是包含所有那些您希望避免从A的接口导出的函数的包。
如果它们是两个不同的库,你可以在一个非常精细的级别上控制暴露的内容和测试的方式。库A将仅仅依赖于B,而不会泄露B的任何细节。AB都可以独立测试。
当然,这需要不同的代码组织,但它可以工作。Lerna等工具简化了JavaScript代码的多包存储库。

旁注

老实说,我不同意AlexSzabó的观点,通过测试使用非导出函数的函数来测试非导出函数并不是真正的单元测试。

jyztefdp

jyztefdp3#

我知道这个问题已经有了答案,但我不喜欢选择的答案,因为它涉及到额外的功能。:)经过一些研究,导致我https://www.jstopics.com/articles/javascript-include-file(所以基本的想法可以在那里找到,并归功于他/她)。我的代码是Typescript,但这应该工作正常的Javascript了。
假设您的源应用位于“app.ts”中,并且您有一个名为“function 1”的私有函数:

// existing private function, no change here
function function1(s :string) :boolean => {
  let result=false; /* code, result=true if good */ return result;
}

// at bottom add this new code
 
// exports for testing only
// make the value something that will never be in production
if (process.env['NODE_DEV'] == 'TEST') {
    module.exports.function1 = function1;
    module.exports.function2 = function2; // add functions as needed
}

我使用Jest来做单元测试,所以在tests/app.test.ts中我们做:

process.env['NODE_DEV'] = 'TEST';
let app = require("../app");

describe("app tests", () => {
  test('app function1', () => {
    expect(app.function1("blah")).toBe(true);
    expect(app.function1("foo")).toBe(false);
  });

  test('app function2', () => {
    expect(app.function2(2)).toBeUndefined();
  });
});

添加任何你需要的测试,它都会测试那些私有函数。它对我很有效,不需要重新布线。

guz6ccqo

guz6ccqo4#

也许是necro-posting,但是我解决这个问题的方法是使用一个“index.js”,它只导出你想公开的函数。
您仍然需要导出私有函数,但是这种方法确实在测试和生产之间增加了一个抽象层。

  • 模块/启动文件.js*
function privateFunction1() {/**/};
function privateFunction2() {/**/};

// Different syntax is a good visual indicator that this is different to public function
exports.privateFunction1 = privateFunction1;
exports.privateFunction2 = privateFunction2;

exports.publicFunction1 = function() {/**/};
exports.publicFunction2 = function() {/**/};
  • 模块/索引.js*
exports.publicFunction1 = require('./startingFile.js').publicFunction1;
exports.publicFunction2 = require('./startingFile.js').publicFunction2;
  • 导入文件.js*
const { publicFunction1, publicFunction2 } = require('./module');

您甚至可以使用NODE_ENV变量来只导出不在生产环境中的私有函数。

42fyovps

42fyovps5#

如何测试快速路由中的专用功能

在我的例子中,我想为快速路由中的私有函数编写单元测试。

问题所在

如果我们这样做,测试工作正常:

const exportedForTesting = {
  privateFunction1,
  privateFunction2,
};

module.exports = {
  router,
  exportedForTesting,
};

不过,快递对此抱怨道:TypeError app.use() requires a middleware function
......因为它希望看到的只是:

module.exports = router;

解决方案

我们需要将map添加为router上的属性:

router.exportedForTesting = {
  privateFunction1,
  privateFunction2,
};

module.exports = router;

然后我们可以访问测试中的私有函数,就像Jordan的答案一样。

const { exportedForTesting } = require('path/to/route/file.js');

const { privateFunction1, privateFunction2 } = exportedForTesting;
myss37ts

myss37ts6#

要测试私有函数,您可以在同一个文件中进行内联测试。测试部分将在绑定过程中移除。一些测试框架(如Vitest)允许这样做。

相关问题