使用Jasmine进行私有方法单元测试

29

我正在使用Jasmine为一个Angular应用编写测试用例。但是许多服务的内部方法被声明为私有的。

例如:

App.service('productDisplay', function(){
    var myPrivate = function(){
        //do sth
    }
    this.doOfferCal = function(product, date){
        //call myPrivate
        //do sth too
        return offer;
    }
});

使用Jasmine编写“doOfferCal”的测试代码很简单,但我想为"myPrivate"编写单元测试。 我该怎么做呢? 预先感谢您的帮助。

如果可能的话,在单元测试中尽量避免访问私有方法。尝试在必要时初始化一切,然后只调用公共方法并期望结果。测试不应该知道服务内部的所有实现。如果将来更改私有方法或将其替换为其他内容但获得相同的结果,则测试将会失败,因为它知道您的私有方法。另一方面,如果您只调用公共方法并仅检查结果,则可以随时更改内部逻辑,测试仍将保持稳定。 - Robert Tab
5个回答

21

感谢jabko87。

另外,如果你想传递参数,请使用以下示例:

const myPrivateSpy = spyOn<any>(service, 'transformNative').and.callThrough();
 myPrivateSpy.call(service, {name: 'PR'});

注意:这里的service是一个类(Class),transformNative是一个私有方法(private method),而{name: 'PR'}是传递一个对象参数。


不错!使用私有方法可以在TypeScript中实现TDD。 - mike mckechnie
这很棒,但是你在 expect(service.nativeMethod). ... 语句中该怎么称呼它? - Micah
该 service.nativeMethod 仍未显示。 - Micah

10

Achan是完全正确的,但如果您真的需要在测试中调用私有方法(这本应该永远不会发生:-)),可以通过以下方式实现:

var myPrivateSpy = spyOn(productDisplayService, "myPrivate").and.callThrough();
myPrivateSpy.call();

为什么在测试中不应该调用私有方法?如果不调用私有方法,那如何测试私有方法呢?我是测试新手。 - Micah
1
@Micah private 方法被 public 方法使用,因此只测试 public 方法就足够了。私有方法是实现细节,只要 public 方法按照预期工作即可。通过测试 private 方法,您还会破坏封装性并创建测试重复(publicprivate 方法的测试用例将存在交集)。 - michal.jakubeczy
谢谢 @Michal - Micah

10

你想测试私有方法有特定的原因吗?

通过测试 doOfferCal(),暗示着你在间接地测试 myPrivate() 是否正确执行。

虽然这是用于RailsConf的内容,但Sandi Metz在如何进行测试方面有一个非常好的演讲。


1
我的“doOfferCal”调用了许多私有方法,并最终返回一个复杂的结果,因此认为测试每个私有方法会很好。顺便问一下,在这种情况下你有什么建议吗? - arnold
3
有一种观点认为私有方法是实现细节。通过测试私有方法,即使公共方法仍在正确运行,您的测试也会出错。结果可能会导致您创建过于脆弱的测试,并使开发人员望而却步,不愿改进/重构实现。 - achan
2
抱歉,我没有意识到按下回车键会提交我的帖子。在这种情况下,我会测试 doOfferCal(),然后验证它返回的复杂对象。如果该测试通过,则中间的所有私有方法都按预期工作。我相信你会找到支持私有方法的人,但就我而言,在编写有用且易于维护的单元测试方面,这是最好的方法。 - achan
3
同时,这将使您能够在不破坏任何单元测试的情况下优化“doOfferCal()”的实现。 - achan
1
我明白你的意思,但我的客户不这样认为。他坚持认为,在任何情况下,我们都必须使用100%的代码覆盖率工具。 - Tomasz Szymanek
1
@TomaszSzymanek 你仍然可以通过测试与你的私有方法交互的公共方法来实现100%的代码覆盖率。 - achan

6

如果您想调用私有方法,只需按照以下方式进行:

component["thePrivateMethodName"](parameters);

在这里,component表示您的服务类或组件类。


我只是为了展示如何调用私有方法而编写了这段代码,但我不建议这样做。如果方法名称更改,此实现可能会变得脆弱,您将不会收到错误提示,只有测试失败,您需要找出原因。 - Robert Tab
谢谢。我敢大胆地展示一个测试私有方法的有效用例:我有一个涉及整合几个功能的故事。之前的工程师公开了一些本应该是私有的方法。它们都有测试。在我进行整合并找出如何适当地测试调用方法的过程中,我希望能够继续测试这些私有方法,以保持测试的运行。这个答案非常完美;直截了当,省去了大量注释,并给了我所需要的红绿重构模式。 - undefined

2
为了测试内部函数,我调用外部函数来调用内部函数,然后根据内部函数的要求改变我的输入。因此,在你的情况下,你会调用productDisplay,根据myPrivate的需求改变你的输入,然后验证你是否有预期的输出。你也可以监听myPrivate并使用.havebeencalledwith.andcallthrough来测试。

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