我有一个带有字典的类,用于缓存来自服务器的特定输入响应。由于这用于缓存目的,因此将其保留为类变量。
class MyClass:
cache_dict = {}
def get_info_server(self, arg):
if arg not in self.cache_dict:
self.cache_dict[arg] = Client.get_from_server(arg)
return cache_dict[arg]
def do_something(self, arg):
# Do something based on get_info_server(arg)
在编写单元测试时,由于字典是一个类变量,因此这些值在测试用例中进行缓存。
# Assume that Client is mocked.
def test_caching():
m = MyClass()
m.get_info_server('foo')
m.get_info_server('foo')
mock_client.get_from_server.assert_called_with_once('foo')
def test_do_something():
m = MyClass()
mock_client.get_from_server.return_value = 'bar'
m.do_something('foo') # This internally calls get_info_server('foo')
如果test_caching
首先执行,则缓存的值将是一些模拟对象。如果test_do_something
首先执行,那么只有一次调用测试用例的断言将会失败。
如何让测试彼此独立,除了直接操作字典(因为这就像需要对代码内部工作的深入了解。如果内部工作要改变怎么办?我需要验证的只是API本身,而不是依赖于内部工作原理)?
答案 0 :(得分:3)
您无法在此处重置缓存。如果您正在 unittesting 这个类,那么您的unittest将需要熟悉该类的内部工作原理,因此只需重置缓存。无论如何,你很少能在不调整单元测试的情况下改变课程的工作方式。
如果您认为这仍然会造成维护负担,那么通过添加类方法使缓存处理显式化:
class MyClass:
cache_dict = {}
@classmethod
def _clear_cache(cls):
# for testing only, hook to clear the class-level cache.
cls.cache_dict.clear()
请注意,我仍然给它一个带有前导下划线的名字;这不是第三方应该调用的方法,它只适用于测试。但现在您已经集中清除缓存,让您可以控制它的实现方式。
如果您使用unittest
框架来运行测试,请在TestCase.setUp()
method中的每次测试之前清除缓存。如果您使用的是不同的测试框架,那么该框架将具有类似的钩子。 在每次测试之前清除缓存可确保您始终处于干净状态。
请注意您的缓存不是线程安全的,如果您与线程并行运行测试,那么您将遇到问题。由于这也适用于缓存实现本身,这可能不是你现在担心的事情。
答案 1 :(得分:0)
您需要使用Python内置的UnitTest TestCase并实现设置和拆卸方法。
如果在测试中定义setUp()
和tearDown()
,则每次调用其中一个测试方法(分别在之前和之后)时都会执行这些操作
示例:
# set up any global, consistent state here
# subclass unit test test case here.
def setUp(self):
# prepare your state if needed for each test, if this is not considered "fiddling", use this method to set your cache to a fresh state each time
your_cache_dict_variable = {}
### Your test methods here
def tearDown(self):
# this will handle resetting the state, as needed
查看文档以获取更多信息:https://docs.python.org/2/library/unittest.html
答案 2 :(得分:0)
您没有明确地将它放在问题中,但我假设您的测试方法位于unittest.TestCase
的子类MyClassTests
。
在被测方法中明确设置MyClass.cache_dict
。如果它只是一本字典,没有任何getter / setter,你就不需要模拟。
如果您想保证每个测试方法都是独立的,请在MyClass.cache_dict = {}
中设置MyClassTests.setup()
。
答案 3 :(得分:-1)
我可以建议您在测试类中使用setUp()
和tearDown()
方法。
from unittest import TestCase
class MyTest(TestCase):
def setUp(self):
self.m = MyClass()
//anything else you need to load before testing
def tearDown(self):
self.m = None
def test_caching(self):
self.m.get_info_server('foo')
self.m.get_info_server('foo')
mock_client.get_from_server.assert_called_with_once('foo')