Python:测试类的单个方法的最佳方法

KFL*_*KFL 8 python testing pytest

我有一个类如下:

class A:
    def __init__(self, arg1, arg2, arg3):
        self.a=arg1
        self.b=arg2
        self.c=arg3
        # ...
        self.x=do_something(arg1, arg2, arg3)
        self.y=do_something(arg1, arg2, arg3)

        self.m = self.func1(self.x)
        self.n = self.func2(self.y)
        # ...

    def func1(self, arg):
        # do something here

    def func2(self, arg):
        # do something here
Run Code Online (Sandbox Code Playgroud)

如您所见,初始化类需要提供arg1,arg2和arg3.但是,测试func1和func2并不直接需要这样的输入,而是它只是一个输入/输出逻辑.

在我的测试中,我当然可以以常规方式实例化和初始化测试对象,然后单独测试func1和func2.但初始化需要输入arg1 arg2,arg3,这与测试func1和func2无关.

因此,我想单独测试func1和func2,而无需先调用__init__.所以我有以下两个问题:

  1. 设计此类测试的最佳方法是什么?(最好是在py.test中)
  2. 我想在调用的情况下测试func1和func2 __init__.我读出这里A.__new__()可以跳过调用__init__,但仍具有类实例化.有没有更好的方法来实现我的需要而不这样做?

注意:

我的问题在这里有两个问题:

  1. 是否有必要测试各个成员的职能?
  2. (用于测试目的)是否有必要在不初始化对象的情况下实例化类__init__

对于问题1,我做了一个快速的谷歌搜索,并找到一些相关的研究或讨论:

我们最初通过设计一个单独测试每个成员函数的测试套件来测试没有父节点的基类,并测试成员函数之间的交互.

对于问题2,我不确定.但我认为有必要,如示例代码所示,调用func1和func2 __init__.我觉得在一个没有被调用过的类A对象上测试它们感觉更舒服__init__(因此之前没有调用func1和func2).

当然,可以用常规方法(testobj = A())实例化一个A类对象,然后对func1和func2进行单独测试.但这是好的:)?我在这里讨论的是测试这种情况的最佳方法是什么,优点和缺点是什么.

另一方面,人们也可能会争辩说,从设计的角度来看,不应该首先调用func1和func2 __init__.这是一个合理的设计选择吗?

Bre*_*arn 7

在没有实例化类(包括运行__init__)的情况下测试类的方法通常没有用,甚至不可能.通常,您的类方法将引用类的属性(例如,self.a).如果您不运行__init__,那么这些属性将不存在,因此您的方法将无法运行.(如果你的方法不依赖于它们的实例的属性,那么为什么他们的方法,而不仅仅是独立的功能是什么?)在您的例子,它看起来像func1func2是初始化过程的一部分,所以他们应该作为一部分进行测试那.

从理论上讲,可以通过使用__new__然后只添加您需要的成员来"准实例化"类,例如:

obj = A.__new__(args)
obj.a = "test value"
obj.func1()
Run Code Online (Sandbox Code Playgroud)

但是,这可能不是一个很好的测试方法.首先,它会导致您复制可能已存在于初始化代码中的代码,这意味着您的测试更可能与实际代码不同步.另一方面,您可能必须以这种方式复制许多初始化调用,因为您必须手动重新执行__init__从您的类调用的任何基类方法将要执行的操作.

至于如何设计测试,您可以查看unittest模块和/或鼻子模块.这为您提供了如何设置测试的基础知识.实际放入测试的内容显然取决于您的代码应该做什么.

编辑:问题1的答案是"肯定是的,但不一定是每一个".你的问题2的答案是"可能不是".即使在您提供的第一个链接中,也存在关于是否应该测试不属于该类的公共API的方法的争论.如果你的func1和func2纯粹是内部方法,只是初始化的一部分,那么可能没有必要在初始化时单独测试它们.

这是关于从内部调用func1和func2是否合适的最后一个问题__init__.正如我在评论中反复提到的那样,这取决于这些功能的作用.如果func1和func2执行初始化的一部分(即,为实例做一些"设置"工作),那么从它们调用它们是完全合理的__init__; 但是在这种情况下,它们应该作为初始化过程的一部分进行测试,而不需要单独测试它们.如果func1和func2 不是初始化的一部分,那么是的,你应该独立测试它们; 但在那种情况下,为什么他们在__init__

构成实例化类的组成部分的方法应作为测试类实例化的一部分进行测试.不应该从内部调用不构成实例化类的组成部分的方法__init__.

如果func1和func2"只是一个输入/输出逻辑"并且不需要访问该实例,那么它们根本不需要是该类的方法; 它们可以只是独立的功能.如果你想将它们保存在类中,你可以将它们标记为staticmethods,然后直接在类上调用它们而不实例化它们.这是一个例子:

>>> class Foo(object):
...     def __init__(self, num):
...         self.numSquared = self.square(num)
...     
...     @staticmethod
...     def square(num):
...         return num**2
>>> Foo.square(2) # you can test the square "method" this way without instantiating Foo
4
>>> Foo(8).numSquared
64
Run Code Online (Sandbox Code Playgroud)

可以想象你可能有一些需要极其复杂的初始化过程的怪物类.在这种情况下,您可能会发现有必要单独测试该过程的各个部分.然而,这样一个巨大的初始序列本身就是一个笨重的设计警告.