我在这里使用 verify(mockito) 。这是测试我的服务的正确方法吗?
I'm using verify(mockito) here. Is it the correct way of testing my service?
我有一个与 Spring 引导中的单元测试相关的问题。我有一个简单的用户管理应用程序,它对用户进行基本的 crud 操作。我使用根据端点在控制器内部调用的服务。例如:
for url = "/createUser" - service.createUser 被调用并处理所有activity(检查用户是否已经存在,以及其他异常处理)并最终将用户添加到数据库中。
现在我要在创建用户的情况下对该服务进行单元测试,以下是我的代码:
@Test
void testRegisterUser() {
Set<String> roles = new HashSet<>();
signUpRequest.setCustomername("s");
signUpRequest.setCustomerid("1s");
signUpRequest.setCustomername("custsomername");
signUpRequest.setDescription("customser description");
signUpRequest.setEmail("customser@mail.com");
signUpRequest.setPassword("12s3");
signUpRequest.setRole(roles);
service.createUser(signUpRequest);
verify(repository, times(1)).save(any(UserModel.class));
}
最后我所做的就是调用服务的方法来创建用户。我嘲笑了这个服务和存储库。在最后一行,我正在验证存储库是否已被立即调用。
我想了解我的做法是否正确?这是测试它的正确方法吗?我的前辈想在这里断言,但是如果 createUser 没有 return 任何值,那么 assertEquals 有什么意义,因为我没有进行集成测试,这里没有数据库。我在这里测试的只是我的逻辑和模仿行为。我将不胜感激一个彻底的答案。提前致谢。
根据我的经验,这里有一些自动化测试指南 -
- 在任何时间点,我们都试图实现 100% 的自动化测试覆盖率。它可以使用集成测试或单元测试。如果可以使用单元测试来测试场景或功能,那么我更愿意选择单元测试而不是集成测试,原因如下 -
- 单元测试总是很快 运行
- 理想情况下,单元测试不应依赖于外部依赖项,例如数据库或外部服务或特殊基础设施。每个单元测试用例都应该专注于测试尽可能小的功能。这将确保您可以在任何时间点 运行 单元测试用例,而不依赖于外部依赖项。我们应该模拟所有其他依赖项作为单元测试的一部分。
- 对于任何未来的功能更改,管理单元测试用例比管理集成测试用例要容易得多。
有时,在我们尝试集成测试用例的那些场景中,仅使用单元测试用例很难实现 100% 的测试覆盖率。如果您的应用程序有 UI 交互,那么仅使用单元测试用例无法测试这些交互,因此我们将使用集成测试用例来涵盖这些场景。
让我们举个例子 - 如果您的服务方法有两种不同的可能场景,并且您的方法是由浏览器上的用户操作调用的。作为集成测试的一部分,您可以涵盖一个负责 UI 功能的场景。您的服务方法中的其他场景可以仅使用单元测试用例来覆盖。这将确保您的整个功能都可以得到测试。
理想情况下,我们会尝试实现 100% 的自动化测试覆盖率,但在某些情况下,当 engineering/infrastructure 成本超过自动化测试用例提供的价值时,我们会尽量避免这种情况。这是一种有意识的权衡。
由于缺乏资源,一些团队可能不会优先考虑 100% 的测试覆盖率。
现在回到你的场景,不看你的服务方法代码很难给你一个满意的答案。您的单元测试用例仅检查您的服务方法是否正在调用仅调用一次的存储库方法。如果这涵盖了应用程序的全部功能,那么一切都很好。没有错误或正确的方法,只要你的单元案例涵盖了最多的功能并且易于维护,那么你就是好的。
My Senior wants assert over here, but what's the point of assertEquals if createUser doesnt return any value since I'm not doing Integration Testing, There is no db in here. All I'm testing here is my logic and mimicking the behavior.
目前您只测试存储库的 save
方法被一些 UserModel
调用。这意味着您不知道 UserModel
是否包含它应该包含的所有值。也许您的服务中存在映射错误,导致 request
模型的值未正确转换并映射到 UserModel
,或者您稍后引入了这样的错误,您的测试将不会显示它。我想这就是学长说他想要断言的意思吧。
你可以做的是使用 mockito 的 ArgumentCaptor
。它可以帮助您获取传递到存储库的 UserModel
,以便您可以进行断言。例如
service.createUser(signUpRequest);
ArgumentCaptor<UserModel> userModelCaptor = ArgumentCaptor.forClass(UserModel.class);
verify(repository, times(1)).save(userModelCaptor.capture());
UserModel userModel = userModelCaptor.getValue();
// Make assertions - I assume that your UserModel has an accessor method for email
assertEquals("customser@mail.com", userModel.getEmail());
然而,您似乎已经编写了一个正确的测试用例(涵盖所有服务线)但是使用这种方法您可能会错过一些用例,例如 -
- 如果在保存SQL级别的数据时发生异常,并抛给服务,即使这样你的测试用例也会验证(当方法被调用时),你将无法覆盖这种情况。
现在假设您已经使用了 CrudRepository , S save(S entity) 方法。
它returns 操作完成后的对象,并在上述情况下抛出异常。
您实际上应该模拟存储库并设置 return ,然后断言肯定的情况,并期望我之前提到的情况出现异常。
希望你觉得这很有用。
我有一个与 Spring 引导中的单元测试相关的问题。我有一个简单的用户管理应用程序,它对用户进行基本的 crud 操作。我使用根据端点在控制器内部调用的服务。例如:
for url = "/createUser" - service.createUser 被调用并处理所有activity(检查用户是否已经存在,以及其他异常处理)并最终将用户添加到数据库中。
现在我要在创建用户的情况下对该服务进行单元测试,以下是我的代码:
@Test
void testRegisterUser() {
Set<String> roles = new HashSet<>();
signUpRequest.setCustomername("s");
signUpRequest.setCustomerid("1s");
signUpRequest.setCustomername("custsomername");
signUpRequest.setDescription("customser description");
signUpRequest.setEmail("customser@mail.com");
signUpRequest.setPassword("12s3");
signUpRequest.setRole(roles);
service.createUser(signUpRequest);
verify(repository, times(1)).save(any(UserModel.class));
}
最后我所做的就是调用服务的方法来创建用户。我嘲笑了这个服务和存储库。在最后一行,我正在验证存储库是否已被立即调用。
我想了解我的做法是否正确?这是测试它的正确方法吗?我的前辈想在这里断言,但是如果 createUser 没有 return 任何值,那么 assertEquals 有什么意义,因为我没有进行集成测试,这里没有数据库。我在这里测试的只是我的逻辑和模仿行为。我将不胜感激一个彻底的答案。提前致谢。
根据我的经验,这里有一些自动化测试指南 -
- 在任何时间点,我们都试图实现 100% 的自动化测试覆盖率。它可以使用集成测试或单元测试。如果可以使用单元测试来测试场景或功能,那么我更愿意选择单元测试而不是集成测试,原因如下 -
- 单元测试总是很快 运行
- 理想情况下,单元测试不应依赖于外部依赖项,例如数据库或外部服务或特殊基础设施。每个单元测试用例都应该专注于测试尽可能小的功能。这将确保您可以在任何时间点 运行 单元测试用例,而不依赖于外部依赖项。我们应该模拟所有其他依赖项作为单元测试的一部分。
- 对于任何未来的功能更改,管理单元测试用例比管理集成测试用例要容易得多。
有时,在我们尝试集成测试用例的那些场景中,仅使用单元测试用例很难实现 100% 的测试覆盖率。如果您的应用程序有 UI 交互,那么仅使用单元测试用例无法测试这些交互,因此我们将使用集成测试用例来涵盖这些场景。
让我们举个例子 - 如果您的服务方法有两种不同的可能场景,并且您的方法是由浏览器上的用户操作调用的。作为集成测试的一部分,您可以涵盖一个负责 UI 功能的场景。您的服务方法中的其他场景可以仅使用单元测试用例来覆盖。这将确保您的整个功能都可以得到测试。
理想情况下,我们会尝试实现 100% 的自动化测试覆盖率,但在某些情况下,当 engineering/infrastructure 成本超过自动化测试用例提供的价值时,我们会尽量避免这种情况。这是一种有意识的权衡。
由于缺乏资源,一些团队可能不会优先考虑 100% 的测试覆盖率。
现在回到你的场景,不看你的服务方法代码很难给你一个满意的答案。您的单元测试用例仅检查您的服务方法是否正在调用仅调用一次的存储库方法。如果这涵盖了应用程序的全部功能,那么一切都很好。没有错误或正确的方法,只要你的单元案例涵盖了最多的功能并且易于维护,那么你就是好的。
My Senior wants assert over here, but what's the point of assertEquals if createUser doesnt return any value since I'm not doing Integration Testing, There is no db in here. All I'm testing here is my logic and mimicking the behavior.
目前您只测试存储库的 save
方法被一些 UserModel
调用。这意味着您不知道 UserModel
是否包含它应该包含的所有值。也许您的服务中存在映射错误,导致 request
模型的值未正确转换并映射到 UserModel
,或者您稍后引入了这样的错误,您的测试将不会显示它。我想这就是学长说他想要断言的意思吧。
你可以做的是使用 mockito 的 ArgumentCaptor
。它可以帮助您获取传递到存储库的 UserModel
,以便您可以进行断言。例如
service.createUser(signUpRequest);
ArgumentCaptor<UserModel> userModelCaptor = ArgumentCaptor.forClass(UserModel.class);
verify(repository, times(1)).save(userModelCaptor.capture());
UserModel userModel = userModelCaptor.getValue();
// Make assertions - I assume that your UserModel has an accessor method for email
assertEquals("customser@mail.com", userModel.getEmail());
然而,您似乎已经编写了一个正确的测试用例(涵盖所有服务线)但是使用这种方法您可能会错过一些用例,例如 -
- 如果在保存SQL级别的数据时发生异常,并抛给服务,即使这样你的测试用例也会验证(当方法被调用时),你将无法覆盖这种情况。
现在假设您已经使用了 CrudRepository
希望你觉得这很有用。