我遇到安排代码以使其易于测试的问题。我的代码中有两个主要模块:缓存生成器和修改器构建器,两者的复杂程度大致相同。修饰符构建器用于缓存生成器的子对象的一种方法。
我已经拥有完整的测试套件,涵盖了修改器构建器的功能。我想添加涵盖缓存生成器的所有功能的测试,但为了显着降低这些测试的复杂性,我需要将修改器构建器替换为一些存根,它根据传递给它的参数返回预定义的“固定数据”。
我的实际问题在于选择使用存根替换实际修改器构建器的方式,这看起来很好,并且仍然便于测试。看看下面的代码:
来自GitHub的代码:
cacheGenerator / generator.py:
class CacheGenerator:
def __init__(self, logger):
...
self._converter = Converter(logger)
def run(self, dataHandler):
...
data = self._converter.convert(data)
cacheGenerator / converter.py:
class Converter:
...
def convert(self, data):
...
self._buildModifiers(data)
def _buildModifiers(self, data):
...
builder = ModifierBuilder(data['expressions'], self._logger)
...
modifiers, buildStatus = builder.buildEffect(...)
用stub替换修饰符构建器有哪些方法?我想至少存在以下几种变体:
1-4中的一些变体看起来是可以接受的,但理想情况下我想保持代码尽可能接近(原始),所以我正在研究存储子对象的替代方法。
答案 0 :(得分:1)
我通常更喜欢2,因为它使意图明确,是一个小改动,它可能对其他重用我的工作的代码有用。
或者,请查看为您构建ModifierBuilder
的{{3}}或dependency injection。
最后,您可以通过导入模块然后为符号分配新值来使用factory:
import cacheGenerator.converter
cacheGenerator.converter.ModifierBuilder = ...
当然,这会更改每个人的符号(也适用于所有其他测试),因此您需要保存旧值并在测试后将其恢复。
如果你对这个解决方案感到不安/不安,那么你是对的:这是一个绝望的措施。如果您真的无法更改原始代码,请将其作为最后的手段使用。
答案 1 :(得分:1)
当我需要在测试中模拟/伪造对象时,我使用Fudge。
在您的情况下,我建议使用patched_context。有了它,您可以修补对Converter
方法的调用。
然后你可以这样做:
补丁调用_converter.convert
test.py:
from cacheGenerator.generator import CacheGenerator
from cacheGenerator.converter import Converter
from fudge import patched_context
import unittest
class Test_cacheGenerator(unittest.testCase):
def test_run(self):
def fakeData(convertself, data):
# Create data to be returned to
# data = self._converter.convert(data)
fakedata = ...
return fakedata
# We tell Fudge to patch the call to `Converter.convert`
# and instead call our defined function
cache = cacheGenerator(...)
with patched_context(Converter, 'convert', fakeData)
cache.run()
或者您可以修补self._buildModifiers
内的Converter
来电:
def test_run(self):
cache = cacheGenerator(...)
def fakeBuildModifiers(convertself, data):
# set up variables that convert._buildModifiers usually sets up
convertself.modifiers = ...
convertself.buildStatus = ...
# We tell Fudge to patch the call to `Coverter._buildModifiers`
# and instead call our defined function
cache = cacheGenerator(...)
with patched_context(Converter, '_buildModifiers', fakeBuildModifiers):
cache.run()
或者,您也可以使用Fudge fake object。
from fuge import Fake
...
def test_run(self):
cache = cacheGenerator(...)
fakeData = ...
fakeConverter = Fake('Converter').provides('convert').returns(fakeData)
# Fake our `Converter` so that our any calls to `_converter.convert` are
# made to `fakeConverter.convert` instead.
cache._converter = fakeConverter
cache.run()
在最后一种情况下,由于您要修补整个_converter
对象,如果您正在调用任何其他方法,则还需要对它们进行修补。
(Fake('Converter'.provides('convert').returns(fakeData)
.provides(....).returns()
.provides(....).returns()
)