一段时间以来,我已经将我的业务逻辑与REST Api层分开进行了单元测试。
在集成测试中,我已经针对服务的API测试了服务本身。
自然地,集成测试并不包括单元测试所包括的所有边缘情况。感觉像是过度杀伤和重复的测试代码。
但是实际上,我剩下的整个图层都没有覆盖。我真的不能确定返回值是否已按预期序列化,错误代码正确或参数是否按我的要求反序列化。
我的问题是,我是否应该放弃通过实现它们的对象来测试业务逻辑,并通过服务进行测试,以便合并所有情况而无需进行重复工作?
注释:
添加了一些示例伪代码来阐明
class Book:
id: int
title: string
class BookRepository:
add_book(book: Book) -> Book
remove_book(id: int) -> bool
all() -> List[Book]
class BookApi:
repository = BookRepository()
@route('/api/books')
get() -> List[Book]
@route('/api/books/id', method=POST)
add(request_body) -> bool :
book = parse_book_from_request(request_body)
return repository.add(book)
@route('/api/books/id', method=DELETE)
delete() -> bool
答案 0 :(得分:1)
从您的描述中,我看到了如下情况:
您有两个单元测试方案,即业务逻辑的单元测试和REST层的单元测试。您还具有一个交互测试方案,即REST层和业务逻辑之间的交互。并且,您有一个子系统测试方案,即对由REST层和业务逻辑一起形成的子系统的测试。
并且,您担心解决所有这些测试方案可能导致的工作量和潜在的冗余。 (嗯,实际上您只提到了业务逻辑单元测试和集成测试,所以我可能使它变得更糟了……)
在这里可以为您提供帮助的是,针对每种测试场景询问自己,可能仍然存在其他测试尚未解决的错误。如果您想到了一个测试用例,但是却想不出该测试可能检测到的任何错误,那么也许就不需要该测试用例,或者它的目的已经用另一种方式解决了。
从下至上:您具有业务逻辑的单元测试。那么,对于REST层,什么单元测试可能有意义?您提到了反序列化,但可能还会进行合理性检查和对格式错误的请求的处理。所有这些都需要彻底的测试-包括出于安全原因的负面测试。考虑一下您可以在隔离的REST层中找到的错误-隔离的业务逻辑的单元测试中显然找不到这些错误。
现在,进入交互测试(也称为集成测试):这些测试的目标仅在于各个部分之间的交互-而不取决于任何一方事后是否做对了事(已通过单元测试进行了测试)。换句话说,测试检查是否以正确的顺序以正确的格式用正确的参数调用了正确的函数,或更普遍地说,是对接口的两面都具有相同的理解。边界情况在这里也很有意义-例如,查看被呼叫者是否可以处理呼叫者提供的极端情况。诚然,如果组件之间的接口比组件大小大,则存在冗余的风险。
但是,可以通过某些方式限制冗余:假定REST层旨在过滤掉无效的book_id
值。您想测试REST层将传递的最大book_id
是否被业务逻辑接受。如果业务逻辑本身检查book_id
,并且如果不被接受,则抛出异常,您的交互测试可以集中于是否抛出异常。您不必验证是否找到了正确的书-当对业务逻辑进行单元测试时,就对本书进行了测试。
然后,子系统进行测试:再一次,考虑一下可能没有捕获到只能从整个子系统来看才能发现的错误。例如,子系统是否满足所有要求,或者某些功能被遗忘了? (单元测试不会找到被遗忘的功能,交互测试可能会-但并非在所有情况下都可以。)可能存在测试可以识别的版本不匹配吗?并且,再次尝试将测试重点放在基本方面,以避免单元测试和交互测试的重复。
对不起,我只能通过一些抽象的例子给出一些抽象的建议。