问题描述
一段时间以来,我已经将我的业务逻辑与 REST Api 层分开进行了单元测试.
在集成测试中,我已经针对其 api 测试了服务本身.
For a while now I have unit tested my business logic separately from the REST Api layer.
In the integration tests I've tested the service itself against its api.
当然,集成测试并不包括单元测试所包含的所有边缘情况.感觉就像一个矫枉过正和重复的测试代码.
Naturally, the integration tests did not include all the edge cases that the unit tests included. It felt like an overkill and duplicate test code.
但实际上,我还剩下一整层没有被覆盖.我真的不能确定返回值是否按预期序列化,错误代码是否正确,或者参数是否按我想要的方式反序列化.
But in fact, I'm left with a whole layer that is not covered. I can't really be sure that the return value is serialized as expected and that the error codes are correct or if the parameters are deserialized as I want.
我的问题是,我是否应该放弃通过实现业务逻辑的对象来测试业务逻辑,而通过服务进行测试,以便在不重复工作的情况下整合所有案例?
My question is, should I just forgo testing the business logic through the objects that implement them, and test through the service in order to incorporate all the cases without duplication of effort?
注意事项:
- 我的意思不是放弃所有单元测试.不直接接触 API 层的足够复杂的单元仍应单独测试.
- 测试运行时在这里不是问题.
添加了一些示例伪代码以阐明
Added some example pseudo code to clarify
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
推荐答案
根据你的描述,我看到的情况如下:
From your description, I see the situation as follows:
您有两个单元测试场景,即业务逻辑的单元测试和 REST 层的单元测试.您还有一个交互测试场景,即 REST 层和业务逻辑之间的交互.并且,你有一个子系统测试场景,即REST层和业务逻辑共同组成的子系统的测试.
You have two unit-testing scenarios, namely the unit-testing of the business logic, and the unit-testing of the REST layer. You also have an interaction test scenario, namely the interaction between the REST layer and the business logic. And, you have a subsystem test scenario, namely the test of the subsystem formed by the REST layer and the business logic together.
而且,您担心解决所有这些测试场景可能会导致工作量和潜在冗余.(嗯,实际上你只提到了业务逻辑单元测试和集成测试,所以我可能把它弄得更糟......)
And, you are concerned about the effort and potential redundancy that might result from having all these test scenarios addressed. (Well, actually you did only mention the business logic unit-tests and integration tests, so I might have made it even a bit worse...)
在这里可以帮助您的是针对这些测试场景中的每一个问自己,其他测试尚未解决的错误可能仍然存在.如果您想到了一个测试用例,但又想不出该测试可能检测到的任何错误,则可能是不需要该测试用例或其目的已经以其他方式解决了.
What can help you here is to ask yourself for each of these testing scenarios, what bugs might still exist that the other tests have not addressed yet. If you think of a test case, but then you can not think of any bug that the test might detect, maybe the test case is not needed or its purpose was already addressed in another way.
自下而上:您有业务逻辑的单元测试.那么,什么单元测试对 REST 层有意义?您提到了反序列化,但也可能会进行合理性检查和处理格式错误的请求.所有这些都需要彻底的测试——包括出于安全原因的负面测试.想想您可以在隔离的 REST 层中找到的错误 - 这些显然无法通过隔离的业务逻辑的单元测试找到.
Going from bottom to top: You have the unit-tests for the business logic. So, what unit-tests could make sense for the REST layer? You mentioned deserialization, but there may also be plausibility checks and handling of malformed requests. All this needs thorough testing - including negative tests for security reasons. Think about bugs that you would be able to find in the isolated REST layer - these are obviously not found by the unit-tests of the isolated business logic.
现在,进入交互测试(又名集成测试):这些测试的目标仅在于各个部分之间的交互——而不是事后各方是否做了正确的事情(通过单元测试进行了测试).换句话说,测试检查是否以正确的顺序和正确的格式使用参数调用正确的函数 - 或者更一般地说,如果接口的双方具有相同的理解.边界情况在这里也很有意义 - 例如查看被调用者是否可以处理调用者提供的极端情况.诚然,如果组件之间的接口与组件尺寸相比较大,则此处存在冗余风险.
Now, entering interaction tests (aka integration tests): The goal of these tests is only on the interaction between the respective parts - not whether any of the parties does the right thing afterwards (which was tested with unit-testing). In other words, the tests check if the right functions are called with the arguments in the right order with the right formats - or, more generally, if both sides of the interface have the same understanding. Boundary cases would make sense here as well - for example to see whether the callee can handle the extreme cases that the caller provides. Admittedly, there is a risk of redundancy here if the interface between the components is large compared to the components size.
但是,您可以通过某些方式限制冗余:假设您的 REST 层旨在过滤掉无效的 book_id
值.您想测试 REST 层将传递的最大 book_id
是否被业务逻辑接受.如果业务逻辑本身检查 book_id
,如果不被接受,则抛出异常,您的交互测试可以关注是否抛出异常.您不必验证是否找到了正确的书 - 这在对业务逻辑进行单元测试时进行了测试.
You can limit the redundancy, however, in certain ways: Assume your REST layer is designed to filter out invalid book_id
values. You want to test if the largest book_id
the REST layer would pass on is accepted by the business logic. If the business logic itself checks the book_id
, and, if it is not accepted, throws an exception, your interaction test could focus on whether an exception was thrown or not. You would not have to verify that the correct book was found - this was tested when the business logic was unit-tested.
然后,子系统测试:再次考虑哪些错误可能没有被捕获,而只有在查看整个子系统时才能发现.例如,子系统是否满足所有要求,还是忘记了某些功能?(单元测试不会找到被遗忘的功能,交互测试可能会 - 但不是在所有情况下.)是否存在测试可以识别的版本不匹配?并且,再次尝试将测试集中在基本方面,以避免单元和交互测试的冗余.
Then, the subsystem tests: Again, think about which bugs might not have been caught that can only be found when looking at the subsystem as a whole. For example, does the subsystem fulfill all requirements or has some feature been forgotten? (Unit-testing will not find forgotten features, interaction testing might - but not in all cases.) Could there be a version mismatch that a test could identify? And, again, try to focus the test on the essential aspects, to avoid redundancy with the unit and interaction tests.
对不起,我只能用抽象的例子给出一个有点抽象的建议.
Sorry that I could only give a somewhat abstract advice with abstract examples.
这篇关于单元测试是否应该针对 REST API 进行测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!