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

时间:2019-02-12 20:55:27

标签: java unit-testing spring-boot integration-testing spring-boot-test

我在Spring Boot中浏览了与测试相关的各种在线教程,并对测试的引用方式感到困惑。

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

相同的问题适用于使用@DataJpaTest的存储库层测试。

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

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

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

}

我的问题是:

  1. 注释@WebMvcTest, @DataJpaTest@SpringBootTest是确定测试类型(Unit还是Integration)还是在测试中使用@MockBean确定吗?
  2. 假设UserControllerTest.java是单元测试,我们在这里使用userRepository模拟@MockBean private UserRepository userRepository依赖关系,而在UserRepositoryTest.java中使用@Autowired private UserRepository userRepository自动装配。为什么??

2 个答案:

答案 0 :(得分:7)

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

在使用@MockBean时,这也让我感到困惑!是将其视为单元测试还是集成测试? 我认为,即使我们使用的是模拟bean,但我们仍在spring上下文中运行,对我而言,这是一个集成测试(因为单元测试不需要在其中运行任何spring上下文)。布兰登提到的同一站点认为@MockBean是集成测试https://www.baeldung.com/java-spring-mockito-mock-mockbean

Image from above site

来自布兰登的回复:“集成测试不应包含任何模拟,并且两种测试都应单独运行。”

如果您要测试从控制器一直到DB的api,但又要排除其他系统(如kafka或外部微服务)怎么办?您将如何实现?您肯定需要@MockBean。这是一个集成测试,即使它具有模拟的bean。

总结(根据我的经验以及几天来搜索和阅读大量矛盾信息后得出的结论)。这是我的意见:

  • 我要说的是,尽可能避免使用spring进行单元测试,而只使用Mockito或不需要spring上下文的其他框架。例如,为服务类编写测试以测试某些计算逻辑时,我们不需要spring上下文,这是PURE单元测试。
  • 我们仍然可以为控制器类编写PURE单元测试。我们可以通过调用控制器中的方法来做到这一点,然后断言这些方法达到了预期的效果(例如,使用正确的参数..etc调用正确的基础方法)。基本上为服务类编写单元测试时,方法相同。 (如果以下类型的测试已经涵盖了这些内容,也许就不需要这些?)
  • 我们仍然可以在没有任何Spring上下文的情况下为api编写纯单元测试。这描述了here。我尝试了,对我有用。我将代码粘贴在帖子的末尾。
  • 在春季环境中运行测试时,这被认为是 集成测试,即使您使用的是@MockBean。一个例子: 如果我们要测试从控制器一直到的api 数据库,但我们想排除其他系统(例如kafka,电子邮件或 其他外部微服务)。我们将如何实现这一目标?我们 肯定需要@MockBean。尽管这是一个集成测试 使用一些模拟豆。
  • 我认为最令人困惑的部分是仅在测试api层时 使用spring作为问题中的UserControllerTest (我 就是说调用api并确保它返回正确的 状态代码和响应格式)。那被认为是单元测试还是 整合测试?它不是单元,因为单元测试不需要弹簧 要在其中运行的上下文。实际上是介于单元和 集成测试。该资料很好地解释了这个概念 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());
    }
    

    }

希望有帮助,请告诉我是否有更好的意见,因为我为此付出了很多努力:)

答案 1 :(得分:0)

使用Spring Boot进行单元测试和集成测试之间的主要区别在于,单元测试可以独立运行,而集成测试将在执行开始之前引导Spring上下文。

要单独运行,需要根据要测试的控制器模拟依赖项。这样,您就可以端对端地测试非常具体的测试用例,而不必担心数据库或服务的开销。因此,使用注释@MockBean。

因此,集成测试着重于集成应用程序的不同层,例如数据库。大多数人都使用内存数据库(例如H2)来测试其存储库。集成测试不应包含任何模拟,并且两种测试都应单独运行。

资源:https://www.baeldung.com/spring-boot-testing