我正在尝试以良好的面向对象的方式设计和测试类似于以下的代码? (或者是一种pythonic方式?)
这是一个工厂类,决定一个人的姓名是长还是短:
class NameLengthEvaluator(object):
def __init__(self, cutoff=10)
self.cutoff = cutoff
def evaluate(self, name):
if len(self.name) > cutoff:
return 'long'
else:
return 'short'
这是一个人类,对自己名字的长度有意见:
class Person(object):
def __init__(self, name=None, long_name_opinion=8):
self.name = name
def name_length_opinion(self):
return 'My names is ' + \
NameLengthEvaluator(long_name_opinion).evaluate(self.name)
几个问题:
Person
方法name_length_opinion()
是否值得进行单元测试,如果是,那会是什么样的?似乎对此方法的任何测试都只会重述其实现,并且测试只是存在以确认没有人触及代码。
(免责声明:代码未经测试,我是python的新手)
答案 0 :(得分:2)
Person方法name_length_opinion()是否值得进行单元测试?如果是,那么它会是什么样子?
你想确保它做你认为它做的事情并确保它未来不会破裂吗?如果是,请为其编写单元测试。
并且测试将存在以确认没有人触及代码
单元测试更多的是确保一个类符合它指定的合同。你不必为所有东西编写单元测试,但如果它是一个简单的方法,它应该是一个简单的单元测试。
似乎对此方法的任何测试都只会重述其实现
你不应该重复算法,你应该使用用例。例如,截止值为NameLengthEvaluator
的{{1}}应该是短名称:
这些名字很长:
因此,您应该验证该方法是否正确报告了这些名称的简短性。您还应该测试截止值为10
的{{1}}报告NameLengthEvaluator
为短,其他报告为4
。
如果您曾编写过一个类,然后编写了一个只运行该类的main方法,以确保它能够执行它应该执行的操作(然后当您移动到另一个类时,将该主方法抛弃) ,你已经写过单元测试了。但是不要扔掉,保存它并将其转换为单元测试,以便将来你可以确保你没有破坏任何东西。
通常,有一种很好的方法来测试具有完全外部功能的类的简单方法
好吧,如果它完全是外部的那么为什么它是该类的方法呢?通常,您至少具有可以测试的某些逻辑。在这种情况下,您可以测试Mary
在正确的情况下返回name_length_opinion
或My names is long
。
答案 1 :(得分:1)
这实际上取决于该代码的生命周期。很明显,在当前状态下,该方法显然是正确的,并且单元测试更多地是它应该如何表现的规范。如果您打算在将来进行更改(例如,以某种方式重新实现NameLengthEvaluator
),那么进行单元测试很有用,因为运行测试会捕获任何回归。但在这种情况下,您似乎不太可能进行任何更改,因此测试可能过多(尽管进行了良好的健全性检查)。
答案 2 :(得分:0)
通常你会在这里使用模拟。你可以创建一个模拟NameLengthEvaluator
,它返回一个记录了它与之连接的对象的对象,当name_length_opinion
返回时,你会检查以确保它被使用并与正确的东西连接。 / p>
例如,使用unittest.mock
:
from unittest.mock import MagicMock, patch
@patch('your_module.NameLengthEvaluator', autospec=True)
def test_person_name_length_opinion(NameLengthEvaluator):
expected_result = object()
opinion = MagicMock(name='opinion')
opinion.__radd__.return_value = expected_result
name_length_evaluator = MagicMock(name='name_length_evaluator')
name_length_evaluator.evaluate.return_value = opinion
NameLengthEvaluator.return_value = name_length_evaluator
name = object()
length_limit = object()
person = Person(name, long_name_opinion=length_limit)
result = person.name_length_opinion()
NameLengthEvaluator.assert_called_with(length_limit)
name_length_evaluator.evaluate.assert_called_with(name)
opinion.__radd__.assert_called_with('My names is ')
assert result is expected_result
然而,由于这个方法很简单,我不确定你在乎那么多。