在JavaScript中测试私有函数

17

我正在使用JavaScript中的模块模式以分离公共接口和私有实现。为了简化我的操作,我的代码生成一个图表。图表由多个部分组成(轴、标签、绘图区、图例等)。我的代码看起来像:

var Graph = function() {
  var private_data;
  function draw_legend() { ... }
  function draw_plot() { ... }
  function helper_func() { ... }
  ...

  return {
    add_data: function(data) {
      private_data = data;
    },
    draw: function() {
      draw_legend()
      draw_plot()
    }
  }
}

有些人主张仅测试类的公共接口,这是有道理的,但我确实想进行一些测试以分别测试每个组件。如果我的draw_legend()函数出错,我希望测试失败的是那个测试而不是公共的draw()函数的测试。我错了吗?

我可以将每个组件都分开放在不同的类中,例如创建一个Legend类。但对于有时只有5-10行代码的东西来说,创建一个类似乎很愚蠢,并且它会变得更加丑陋,因为我需要传入大量私有状态。并且我将无法测试我的帮助函数。我应该这样做吗?我应该妥协并且仅测试公共的draw()函数吗?还是有其他解决方案?


这可能会有所帮助如何在JavaScript中对私有函数进行单元测试 - Amir Nissim
6个回答

9

无法从外部访问内部函数(私有函数)。如果您想测试内部函数,可以考虑添加一个仅用于测试目的的公共方法。如果您正在使用某种构建环境,例如Ant,则可以预处理JavaScript文件以供生产并删除这些测试函数。

实际上,JavaScript是一种面向对象的语言。只是它不是静态类型的。


6
实际上,JavaScript 并不是面向对象的,它是基于原型的。它只能模仿面向对象的行为。 - pcjuzer
12
@pcjuzer,JavaScript 绝对是面向对象的,只是它的实现方式与大多数人熟悉的经典面向对象语言不同。 - Brian Wigginton
如何添加一个公共测试方法而不失去所有的安全性? - Johnathon Sanders
1
@Johnathon 根据我的研究,最好的想法是拥有一些允许它的调试标志。http://www.mikeball.us/blog/how-to-make-testable-private-functions-in-javascript 我自己还没有使用过这个。 - jwl
如果您想测试内部函数,可以考虑添加一个仅用于测试目的的公共方法。同意@JohnathonSanders的观点,尽管s/security/proper factoring/g。(没有向用户提供的JavaScript是安全的。)这个想法是通过调用公共函数来进行单元测试;您只是将代码拆分成了自我注释(希望如此)的私有块。如果您无法通过调用公共函数并观察模拟来测试私有函数,则存在奇怪的副作用,最好是严重的代码气味。 - ruffin

5

我的解决方案有点小技巧。QUnit示例:

在QUnit测试html的顶部,我声明了:

var TEST_AVAILABLE = true;

在可测试的类中,我有一个类似这样的片段:

在可测试的类中,我有一个类似这样的片段:

if(TEST_AVAILABLE){
   this.test={
      hasDraft:hasDraft,
      isInterpIdIn:isInterpIdIn,
      // other private methods
   };
}

在QUnit中,您可以验证:
test( "hello booth", function() {
  var b = new Booth();
  ok(b);
  ok(b.test);
  ok(!b.test.hasDraft());
});

类似于此处的建议http://www.mikeball.us/blog/how-to-make-testable-private-functions-in-javascript - jwl
@jlarson的URL似乎已更改为http://www.mikeball.us/blog/testable-private-methods-in-javascript/。 - Chris Marisic

3

我有一个类似的问题。我想出来的解决方案并不是我喜欢的,但它完成了任务,我找不到更好的解决方案。

function Graph()
{
    this.Test = function _Test(expressionStr) { return eval(expressionStr); }

    var private_data;
    function draw_legend() { ... }
    function draw_plot() { ... }
    function helper_func() { ... }
    ...
}

测试方法:

var g = new Graph();
g.Test("helper_func()") == something;
g.Test("private_data") == something2

1

其实有一种简单的方法。您可以使用ajax加载脚本并注入一个公开私有函数的函数。我有一个示例在这里,它使用了qUnit和jQuery。但我相信同样可以很容易地使用纯JavaScript完成。


如果你想对闭包编译(或者混淆等)后的版本进行单元测试,这就是一个问题。我只测试编译后的版本 - 如果编译有问题,我需要知道。 - Camilo Martin

0
在面向对象的语言中,通常通过测试类继承被测试类来对受保护方法进行单元测试。
当然,JavaScript并不是真正的面向对象语言,这种模式也不允许继承。
我认为你要么需要将方法设为公共的,要么放弃对它们进行单元测试。

在C#中,派生类无法访问其基类的私有成员。其他语言是否也不同? - John Saunders
在Java中,私有成员或方法也无法被子类访问。 - Stephen P
12
严谨地说,JavaScript 确实是面向对象的,但它不是基于类(class-based)的,而是基于原型(prototype-based)的。 - Neall

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接