Spring Boot 中的单元测试或集成测试

Nit*_*tal 8 java integration-testing unit-testing spring-boot spring-boot-test

我在网上查看了与测试相关的各种教程,Spring Boot并对测试的引用方式感到困惑。

有些文章将使用@WebMvcTest注释的控制器测试称为 as,Unit Test而有些则将其称为Integration Test. 不确定哪一个是正确的。

同样的问题适用于使用@DataJpaTest.

我在我的应用程序中编写了以下两个测试,一个用于控制器,另一个用于存储库。

在底部,我对两者都有一些疑问。请指导。

用户控制器测试.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());
    }

}
Run Code Online (Sandbox Code Playgroud)

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());
    }

}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 注释@WebMvcTest, @DataJpaTest@SpringBootTest决定了测试的类型(UnitIntegration)还是@MockBean在测试中的使用决定了它?
  2. 假设这UserControllerTest.java是一个单元测试,我们在userRepository这里模拟依赖关系,@MockBean private UserRepository userRepository而在UserRepositoryTest.java我们使用 @Autowired private UserRepository userRepository. 为什么 ??

Mos*_*eer 9

为什么需要spring来做单元测试?您只能使用 Mockito 来执行此操作,而无需启动 spring 上下文。在这里详细解释和讨论:https : //reflectoring.io/unit-testing-spring-boot/

在使用@MockBean 时,我也很困惑!这被认为是单元测试还是集成测试?在我看来,即使我们使用的是模拟 bean,但我们仍然在 spring 上下文中运行,对我来说这是一个集成测试(因为单元测试不需要任何 spring 上下文来运行)。Brandon 提到的同一站点将 @MockBean 视为集成测试https://www.baeldung.com/java-spring-mockito-mock-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 上下文来运行。它实际上介于单元测试和集成测试之间。这个来源很好地解释了这个概念 https://blog.marcnuri.com/mockmvc-spring-mvc-framework/ (更具体地说是 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());
    }
    
    Run Code Online (Sandbox Code Playgroud)

    }

希望有所帮助,如果有更好的意见,请告诉我,因为我为此苦苦挣扎:)

  • 你的帖子中的问题3。您正在描述 e2e 测试。您正在测试 Web 层、服务层和域层!只需编写模拟您所描述的依赖项的单元测试即可。如果您必须使用@MockBean,那么这是一个 int 测试。问题 4。这取决于您的用例……您可以选择其中之一。RestAssuredMockMVC 是一个很棒的资源,它允许您将测试设置为单元测试或集成测试!取决于您是否需要 Spring Web 上下文。超级方便的框架,放在你的工具带里!查看:https://www.baeldung.com/spring-mock-mvc-rest-assured。干杯伙计们! (2认同)

Bra*_*don 7

单元测试独立运行,而集成测试在执行开始之前引导 Spring Web 上下文。

单元测试

隔离运行有时需要您根据正在测试的类来模拟依赖项。通过这样做,您可以端到端地测试非常具体的测试用例,而不必担心服务或域层的开销。因此,使用 Mockito 或更具体地说,使用 Mockito.mock() 方法来模拟对象类,并且不会替换 Web 上下文中的任何对象,例如 @MockBean。

集成测试

而集成测试侧重于集成应用程序的不同层,例如数据库。关于数据库,大多数人使用内存数据库(例如 H2)来测试他们的域层/存储库。集成测试不应包含任何模拟,并且两种类型的测试应单独运行。这并不是说集成测试不能包含任何模拟,但这并不常见,因为您已经有了独立的单元测试来测试包含模拟依赖项的应用程序的各个层!

端到端测试

如果您正在从端到端测试您的应用程序,那么最好不要模拟数据以外的任何内容,并进行适当的清理。Cucumber 等测试框架非常适合端到端测试。为什么要模拟不同的层,您已经有了针对该类型测试的单元测试!

资源:https://www.baeldung.com/spring-boot-testinghttps://www.baeldung.com/java-spring-mockito-mock-mockbean