如何对设置内部数据但不返回的方法进行单元测试?

时间:2016-07-22 15:07:36

标签: python unit-testing

从我所读到的,单元测试应该一次只测试一个函数/方法。但是我不清楚如何测试只设置没有返回值的内部对象数据来测试的方法,就像下面Python类中的setvalue()方法一样(这是一个更复杂的简单表示):

class Alpha(object):

    def __init__(self):
        self.__internal_dict = {}

    def setvalue(self, key, val):
        self.__internal_dict[key] = val

    def getvalue(self, key):
        return self.__internal_dict[key]

如果单元测试法规定我们应该一次测试一个函数,那么我该如何测试setvalue()方法呢?一个“解决方案”是将我传入setvalue()的内容与getvalue()的返回进行比较,但如果我的断言失败,我不知道哪个方法失败 - 是setvalue()还是getvalue()?另一个想法是将我传入setvalue()的内容与对象的私有数据__internal_dict [key]进行比较 - 一个巨大恶心的黑客!

截至目前,这是我解决此类问题的方法,但如果断言提出,则只表示我的两个主要方法中有一个没有正常工作。

import pytest

def test_alpha01():
    alpha = Alpha()
    alpha.setvalue('abc', 33)

    expected_val = 33
    result_val = alpha.getvalue('abc')

    assert result_val == expected_val

帮助表示赞赏

2 个答案:

答案 0 :(得分:9)

误解

你在这里遇到的真正问题是你正在做一个错误的前提:

  

如果单位测试法规定我们应该每次测试一个功能......

这根本不是什么好的单元测试。

良好的单元测试是将代码分解为逻辑组件,将它们置于受控环境中并测试其实际行为是否符合预期的行为 - 来自消费者的观点

那些“单位”可能(取决于你的环境)匿名函数,单个类或紧耦合类的集群(并且不要让任何人告诉你类耦合本质上是坏的;有些类是一起进行的)。

要问自己重要的是 - 消费者关心什么

他们当然关心的是 - 当他们调用 set 方法时 - 设置了一些他们甚至无法访问的内部私有成员。< / p>

解决方案

天真地,通过查看您的代码,消费者关心的是,当他们为特定密钥调用setvalue时,为同一个密钥调用getvalue会使他们返回值他们投入了。如果这是单位(班级)的预期行为,那么那就是你应该测试的。

只要行为正确,没有人应该关心幕后发生的事情。

但是,我还会考虑是否真的是这个课程的所有内容 - 这个价值还有什么影响呢?从你的问题中的例子中说出来是不可能的,但不论它是什么,都应该进行测试。

或许,如果这很难定义,这个类本身并不是很有意义,你的“单位”实际上应该是一个独立的小类集合,当它们放在一起时才真正有意义的行为,应该是如此测试。

然而,这里的平衡是微妙的,如果没有更多的背景,很难不那么神秘。

陷阱

你当然不应该(从来没有)做的是让你的测试围绕对象的内部状态。这有两个非常重要的原因:

首先,如前所述,单元测试是关于客户所感知的单位行为。测试它做了我认为它应该作为消费者做的事情。我不 - 也不应该 - 关心它是如何做到的。那本字典与我无关。

其次,良好的单元测试允许您验证行为,同时仍然可以自由地更改行为的实现方式 - 如果将测试绑定到该字典,它将不再是实现细节并成为合同的一部分,意味着对此单元的实施方式所做的任何更改都会强制您保留该字典或更改测试。

这条道路与单元测试的目的相反 - 无痛维护。

最重要的是,消费者 - 以及您的测试 - 并不关心setvalue是否更新内部字典。弄清楚他们真正关心的是什么并测试它。

顺便说一句,这就是TDD(特别是测试优先)真正发挥作用的地方 - 如果你预先通过测试表明预期的行为,很难发现自己被困在“我想要的是什么”测试?”车辙。

答案 1 :(得分:3)

我会考虑在你的测试中访问你的内部数据结构而不是“恶心的黑客”,因为它一次测试一个函数,你几乎立即知道什么是错误的。

我承认从测试中访问私有成员并不是一个好主意,但我仍然认为这会增加更多价值:

class Alpha(object):
    def __init__(self):
        self._internal_dict = {}

    def setvalue(self, key, val):
        self._internal_dict[key] = val

    def getvalue(self, key):
        return self._internal_dict[key]


def test_alpha_setvalue():
    alpha = Alpha()
    alpha.setvalue('abc', 33)
    assert alpha._internal_dict['abc'] == 33


def test_alpha_getvalue():
    alpha = Alpha()
    alpha._internal_dict['abc'] = 33
    assert alpha.getvalue('abc') == 33

请注意,此方法要求您为内部数据结构使用单个下划线,以便测试能够访问它。遵循的惯例是向其他程序员表明它是非公开的。

有关python docs中的更多信息:https://docs.python.org/3/tutorial/classes.html#tut-private