单元测试设计和模拟



假设我有下面的类,我想测试它。

class SearchRecommended:
def __init__(self, request2template):
self._r2t = request2template
def handle(self, request: Request):
return request.user().queries().add_recommendation_query().run(1).print(
RecommendedSearchMedia(self._r2t(request))
).message(RecommendedSearchMessage)

.user()返回的对象属于User"接口",与数据库相关。

class User(Equalable, ABC):
@abstractmethod
def user_id(self):
pass
@abstractmethod
def lang(self):
pass
@abstractmethod
def queries(self) -> "UserQueries":
pass
@abstractmethod
def subscriptions(self) -> "UserSubscriptions":
pass
@abstractmethod
def notifications(self) -> "UserSubsNotifications":
pass
@abstractmethod
def access(self) -> "UserAccess":
pass
def repr(self):
return self.user_id()

UserQueriesUserSubscriptionsUserSubsNotificationsUserAccess也是数据库交互类的基类。

据我所知,单元测试是为了快速,不应该使用实际的数据库连接。单元测试也不应该对他们正在测试的代码的内部结构了解太多。

模拟整个数据库交互层是乏味的,但只模拟正在测试的方法中使用的方法似乎对内部代码"了解太多"。

我在.handle方法中的代码难道不应该自由地从User接口(或它被嘲笑的对象(和后续的持久层类(只要这些调用对给定的接口是正确的(中调用任何它喜欢的方法吗,除非我明确测试调用的方法的顺序?

我是不是做错了什么&我该怎么办?

您的方法handle不适合在单元测试中进行测试。handle所做的唯一一件事就是与其他代码交互。但是,对于测试与其他代码的交互,您更倾向于使用集成测试。

背景是,在任何类型的测试中,你的目标都是找到bug。通过单元测试,您可以尝试在隔离的代码中找到错误。但是,如果你真的隔离了你的代码,你会发现什么bug?

代码中的错误更多地指向"我是否以正确的顺序用正确的参数调用其他对象的正确方法,返回值是否会是我期望的形式"。所有这些问题都不会通过单元测试来回答,而是通过集成测试来回答。

您的单元需要确保类做了它应该做的事情。为了实现这一点,您的类需要某些东西才能发挥作用,在本例中是类User的一个版本。

你的类对User有足够的了解,可以调用它的方法和方法的结果,所以你的测试有足够的资源让这些调用按预期工作。

你的mock实际上不必制作一个虚假的数据库,也不必具有真实的功能,它只需要看起来像它。如果您只关心确保数据层按顺序调用,只需让函数链的每个步骤都将var设置为true或其他值,并在测试结束时验证所有var是否为true。虽然不太好,但它可以确保此类按预期调用数据层。

从长远来看,如果您一直需要做这样的事情,请对User或类似类进行双重测试,并根据需要添加功能。

最新更新