问题描述
我在网上查看了与Spring Boot
测试相关的各种教程,但对测试的引用方式感到困惑。
有些文章将使用@WebMvcTest
注释的控制器测试称为Unit Test
而有些文章将其称为Integration Test
。
不确定哪一个是正确的。
同样的问题适用于使用@DataJpaTest
Repository 层测试。
我在我的应用程序中编写了以下两个测试,一个用于控制器,另一个用于存储库。
在底部,我对两者都有一些疑问。 请指导。
用户控制器测试.java
@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserRepository userRepository;
@Test
public void signUp() throws Exception {
this.mockMvc.perform(get("/signup")).andExpect(status().isOk());
}
}
UserRepositoryTest.java
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@Test
public void whenFindByName_thenReturnEmployee() {
// given
User u = new User();
u.setName("ab");
u.setEmail("ab@cd.com");
entityManager.persistAndFlush(u);
// when
Optional<User> user = userRepository.findById(1L);
// then
assertTrue(user.isPresent());
}
}
我的问题是:
-
注释
@WebMvcTest, @DataJpaTest
或@SpringBootTest
是否确定了测试类型(Unit
或Integration
),还是在测试中使用了@MockBean
来确定它? -
假设
UserControllerTest.java
是一个单元测试,我们在这里使用@MockBean private UserRepository userRepository
userRepository
依赖@MockBean private UserRepository userRepository
而在UserRepositoryTest.java
我们使用@Autowired private UserRepository userRepository
自动装配它。 为什么 ??
1楼
为什么需要spring来做单元测试? 您只能使用 Mockito 来执行此操作,而无需启动 spring 上下文。 在这里详细解释和讨论: :
在使用@MockBean 时,我也很困惑! 这被认为是单元测试还是集成测试? 在我看来,即使我们使用的是模拟 bean,但我们仍然在 spring 上下文中运行,对我来说这是一个集成测试(因为单元测试不需要任何 spring 上下文来运行)。 Brandon 提到的同一站点将 @MockBean 视为集成测试 。
Brandon 回应:“集成测试不应该包含任何模拟,两种类型的测试都应该单独运行。”
如果你想测试一个从控制器开始一直到数据库的 api,但又想排除其他系统(比如 kafka 或外部微服务)怎么办? 你将如何实现这一目标? 你肯定需要@MockBean。 这是一个集成测试,即使它模拟了 bean。
总而言之(根据我的经验以及在搜索和阅读了很多天的矛盾信息之后)。 这是我的意见:
- 我会说,尽可能避免使用 spring 进行单元测试,而只使用 Mockito 或其他不需要 spring 上下文的框架。 例如,在为服务类编写测试以测试某些计算逻辑时,我们不需要 spring 上下文,这是一个 PURE 单元测试。
- 我们仍然可以为控制器类编写 PURE 单元测试。 我们可以通过调用控制器中的方法来做到这一点,然后断言这些方法做了预期的事情(例如,使用正确的参数调用正确的底层方法......等)。 为服务类编写单元测试时的方式基本相同。 (如果以下类型的测试已经涵盖这些内容,也许不需要这些?)
- 我们仍然可以在没有任何 spring 上下文的情况下为 api 编写纯单元测试。 这描述。 我试过了,它对我有用。 我会把代码贴在帖子的最后。
- 在 spring 上下文中运行测试时,即使您使用 @MockBean,这也被视为集成测试。 一个例子:如果我们想测试一个从控制器开始一直到数据库的 api,但我们想排除其他系统(如 kafka、电子邮件或其他外部微服务)。 我们将如何实现这一目标? 我们绝对需要@MockBean。 这是一个集成测试,即使它使用了一些模拟 bean。
我认为最令人困惑的部分是在问题中仅使用 spring 作为 UserControllerTest测试 api 层时(我的意思是调用 api 并确保它返回正确的状态代码和响应格式)。 这被认为是单元测试还是集成测试? 它不是一个单元,因为单元测试不需要 spring 上下文来运行。 它实际上介于单元测试和集成测试之间。 这个来源很好地解释了这个概念 (更具体地说是 MockMvc 独立设置)所以我认为,它可以追溯到团队在哪里放置这些测试(在单元测试文件夹,在集成测试文件夹中,在一个单独的文件夹中?)还需要使用一个好的命名约定,以避免与纯单元测试或同一类的纯集成测试混淆。 据我所知,大多数团队都会考虑这些单元测试,但我不确定这是否是最佳实践!
//unit test to call an api using MockMvc and mockito only @RunWith(MockitoJUnitRunner.class) public class UserControllerTest { private MockMvc mockMvc; @Mock UserService userService; @InjectMocks UserController controllerUnderTest; @Before public void setup() { MockitoAnnotations.initMocks(this); mockMvc = MockMvcBuilders.standaloneSetup(controllerUnderTest).build(); } @Test public void testGetUser() throws Exception { //given: when(userService.getUser(.......)).thenReturn(....); //when: String url = "http://localhost:8081/api/ ....your url"; //then: this.mockMvc.perform(get(url)).andDo(print()).andExpect(status().isOk()); }
}
希望有所帮助,如果有更好的意见,请告诉我,因为我为此苦苦挣扎:)
2楼
单元测试独立运行,而集成测试在执行开始前引导 Spring Web 上下文。
单元测试
孤立运行有时需要您根据正在测试的类来模拟依赖项。 通过这样做,您可以端到端地测试非常具体的测试用例,而不必担心服务或域层的开销。 因此,使用 Mockito 或更具体地说,使用 Mockito.mock() 方法模拟对象类并且不会替换 Web 上下文中的任何对象,例如 @MockBean。
集成测试
而集成测试侧重于集成应用程序的不同层,例如数据库。 关于数据库,大多数人使用内存数据库(如 H2)来测试他们的域层/存储库。 集成测试不应该包含任何模拟,并且两种类型的测试都应该单独运行。 这并不是说集成测试不能包含任何模拟,但它并不常见,因为您已经有独立的单元测试来测试包含模拟依赖项的应用程序的各个层!
端到端测试
如果您正在端到端地测试您的应用程序,最好不要模拟除数据以外的任何内容,并进行适当的清理。 Cucumber 等测试框架非常适合端到端测试。 你为什么要模拟不同的层,你已经有了用于这种类型测试的单元测试!
资源: : 和