模拟还是桩?

4 投票
1 回答
1079 浏览
提问于 2025-04-16 18:56

我有一个方法,它里面调用了另外两个方法。

def main_method(self, query):
  result = self.method_one(query)
  count = self.method_two(result)
  return count

def method_one(self, query):
  #Do some stuff based on results.
  #This method hits the database.
  return result

def method_two(self, result):
  #Do some stuff based on result.
  #This method also hits the database.
  return count

我对单元测试不是很熟悉,也从来没有用过模拟对象和桩对象。

我不太确定怎么为我的第一个方法写单元测试。因为method_one和method_two会多次访问数据库,而且这些操作非常耗费资源,所以我决定使用mox来创建一个模拟对象或桩对象,这样就可以避免直接访问数据库。

如果有经验的人能给我一些关于如何在我的情况下使用模拟对象和桩对象的建议,我会非常感激。

1 个回答

5

在担心测试 main_method() 之前,先测试一下那些小方法。比如说 method_one()。为了方便讨论,假设它在一个这样的类里:

class Foo(object):
    def method_one(self, query):
        # Big nasty query that hits the database really hard!!
        return query.all()

为了测试这个方法而不去碰数据库,我们需要一个能够对 all() 方法做出响应的对象。比如说:

class MockQuery(object):
    def all(self):
        return [1,2]

现在我们可以进行测试了:

f = Foo()
q = MockQuery()
assert f.method_one(q) == [1,2]

这只是一个基本的例子。实际上,情况往往更复杂。为了让写测试变得值得,你的模拟 all() 方法可能需要做一些比简单返回一个常量更有趣的事情。类似地,如果 method_one() 里有很多其他逻辑,我们的 MockQuery 可能需要更复杂一些——也就是说,能够对更多的方法做出适当的响应。通常在测试代码时,你会发现自己最初的设计太复杂了:你可能需要把 method_one() 拆分成更小、更明确的部分,这样更容易测试。

再进一步,你可能会创建一个 MockFoo 类,它能够以简化的方式对 method_one()method_two() 做出响应。

撰写回答