1

我正在尝试以面向对象的方式设计和测试类似于以下的代码?(或者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 新手)

4

3 回答 3

2

单元测试

Person 方法 name_length_opinion() 是否值得进行单元测试,如果需要,它会是什么样子?

你想确保它做你认为它做的事情,并确保它在未来不会崩溃吗?如果是这样,请为它编写一个单元测试。

并且测试只是为了确认没有人接触代码

单元测试更多的是确保一个类符合它指定的契约。您不必为所有内容编写单元测试,但如果它是一个简单的方法,那么无论如何它应该是一个简单的单元测试。

重复

似乎对这种方法的任何测试都只会重述它的实现

你不应该重复算法,你应该使用用例。例如,一个NameLengthEvaluator带有截止的 a10应该有这些短名称:

  • 乔治
  • 玛丽

这些是长名称:

  • 麦克刀
  • 开膛手杰克

因此,您应该验证该方法是否正确报告了这些名称的简短性。您还应该测试NameLengthEvaluator带有截止值的 a4将报告Mary为短而其他的报告为长。

一次性代码?

如果你曾经写过一个类,然后写了一个 main 方法来运行这个类以确保它做它应该做的事情(然后当你移动到另一个类时你把那个 main 方法扔掉了),你已经写了一个单元测试。但是不要扔掉,而是保存它并将其转换为单元测试,以便将来您可以确保没有破坏任何东西。

外部代码

一般来说,有没有一种好方法来测试具有完全外部功能的类的简单方法

好吧,如果它完全是外部的,那么为什么它是那个类的方法呢?通常你至少有一些可以测试的逻辑。在这种情况下,您可以测试name_length_opinion返回My names is longMy names is short在正确的情况下。

于 2013-06-09T04:10:36.143 回答
1

这实际上取决于该代码的生命周期。很明显,在当前状态下,该方法显然是正确的,并且单元测试更多地是对其行为方式的规范。如果您计划在未来进行更改(NameLengthEvaluator例如以某种方式重新实现),那么进行单元测试非常好,因为运行您的测试将捕获任何回归。但在这种情况下,您似乎不太可能进行任何更改,因此测试可能过度(尽管是一个很好的健全性检查)。

于 2013-06-09T04:02:23.247 回答
0

通常你会在这里使用模拟。您可以制作一个NameLengthEvaluator返回一个对象的模拟,该对象记录了与它连接的内容,当name_length_opinion返回时,您将检查以确保它被使用并与正确的东西连接。

例如,使用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

但是,由于该方法非常简单,我不确定您是否在意。

于 2013-06-09T04:13:41.940 回答