如何对方法对象中的方法进行单元测试?

6
我已经执行了Beck描述的“使用方法对象重构方法”重构。现在,我有一个带有“run()”方法和一堆成员函数的类,这些成员函数将计算分解为较小的单元。如何测试这些成员函数?
我的第一个想法是将我的单元测试基本上复制为“run()”方法(使用不同的初始化),但在每次调用成员函数之间添加断言来检查计算状态。 (我正在使用Python和unittest模块。)
class Train: 

    def __init__(self, options, points): 
        self._options = options 
        self._points = points 
        # other initializations

    def run(self): 
        self._setup_mappings_dict() 
        self._setup_train_and_estimation_sets() 
        if self._options.estimate_method == 'per_class': 
            self._setup_priors() 
        self._estimate_all_mappings() 
        self._save_mappings() 

    def _estimate_all_mappings(): 
        # implementation, calls to methods in this class

    #other method definitions

我对在run()方法的实现中调用不同方法之前和之后成员属性状态有期望值。我是否应该对这些“私有”属性进行断言?我不知道如何对这些方法进行单元测试。

另一个选择是我真的不应该测试这些属性。


一些代码或伪代码将有助于理解你的问题。根据你所写的内容,看起来你可能会从unittest.TestCase.setUp()方法中受益。http://docs.python.org/library/unittest.html#unittest.TestCase.setUp - msw
我已经用你提供的代码编辑了你的问题。你能否检查缩进是否正确,必要时重新编辑? - ire_and_curses
3个回答

10

我来回答自己的问题。经过一些阅读和思考,我认为我不应该对这些私有方法进行单元测试。我应该只测试公共接口。如果执行内部处理的私有方法足够重要,值得独立测试,并且不仅仅是当前实现的偶然性,那么也许这是它们应该被重构为一个单独类的标志。


1
如果一个代码单元不适合进行单元测试,那么或许它就不是一个“单元”,对于自我回答并且回答还相当不错的人来说,加1。 - msw
强烈不同意。有很多理由将方法隐藏为私有的,即使这些方法具有重要的功能,对于独立于公共API的测试也是有帮助的。而且Python确实允许您测试受保护/私有方法-您只需要了解名称混淆即可。 - Nathaniel Ford

8

我喜欢你的回答,但我不同意。

使用这种设计模式的情况是,存在一个相当复杂的操作。因此,能够验证此类操作的各个组件,我认为是非常有必要的。

然后,您还需要考虑对其他资源的依赖关系(在这种情况下可能存在或不存在)。

您需要能够使用某种控制反转来注入某种模拟以隔离该类。

此外,大多数模拟框架将为您提供访问私有成员的访问器。


谢谢你提供这个不同的观点。当我提出这个问题时,我还不知道模拟的存在。 - user334856
1
我也改变了我的想法,更接近于你的想法。如果需要测试私有方法,那么它可能应该移动到另一个类中。但是,我认为我的想法仍然适用于应该是内部而不是私有的东西。 - John Nicholas
我建议不要在编写类时使用控制反转(Inversion-of-Control),特别是在Python中,除非有明确的用例,例如将决策引擎包装在通用对象中。这可能会导致代码非常混乱,如果你这样做的唯一原因是为了测试,那么有更直接的方法来解决它(测试私有函数或猴子补丁函数调用)。 - Nathaniel Ford

0

这里涉及到两个原则。首先,公共方法应该是你想要暴露的公共API。在这种情况下,暴露`run()`是合适的,而暴露`estimate_all_mappings()`则不合适,因为你不希望其他人调用那个函数。

其次,一种功能只应该做一件事。在这种情况下,`run()`组装了几个其他复杂操作的结果。`estimate_all_mappings()`正在执行其中一个复杂操作。它可能又把一些其他函数委托给`estimate_map()`这样的函数来进行单个估计,而`estimate_all_mappings()`则聚合这些估计。

因此,有这种责任委派是正确的。那么需要知道如何test a private method

唯一需要创建另一个类的原因是如果有一些子集的功能组成自己的行为单元。除非有一个作为对象传递更容易的状态单元,否则您不会为只由类A调用/使用的类B创建某些类。


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