如何 运行 使用 Spring Boot 2 进行单元测试

How to run a unit test using the SpringBoot 2

我有以下测试:

@SpringBootTest
@ExtendWith(SpringExtension.class)
class BookServiceImplTest {
    @MockBean
    private BookRepository bookRepository;
    @MockBean
    private LibraryService libraryService;

    @Autowired
    private BookServiceImpl bookService;

    @Test
    void create() {
        BookRequestDTO bookRequestDTO = new BookRequestDTO();
        Library library = new Library();
        Book expectedBook = new Book();
        when(libraryService.getById(bookRequestDTO.getLibraryId()))
                .thenReturn(library);
        when(bookRepository.save(any(Book.class)))
                .thenReturn(expectedBook);

        Book actualBook = bookService.create(bookRequestDTO);

        assertEquals(expectedBook, actualBook);
    }
}

没关系,它 运行s,但我想知道有没有办法 运行 将其作为单元测试而不是集成测试,并且仍然使用 @MockBean @Autowired。还是我漏掉了什么?

我试着只留下@ExtendWith(SpringExtension.class),但我得到一个关于未找到 BookServiceImpl bean 的异常。

我知道如何使用 MockitoExtension 和@Mock、@InjectMocks 来做到这一点,但我想知道是否有更多的 SpringBoot 方法来做到这一点?

  1. 删除 @SpringBootTest,这将加载整个上下文并减慢您的测试速度。 @MockBean 的作用是从指定的bean 中创建一个mock 并将其添加到上下文中。由于没有上下文 运行,因此没有必要使用 @MockBean

  2. @RunWith(SpringRunner.class)

  3. 注释你的单元测试class
  4. 对于注入依赖项,这是一个很好的做法,可以显式创建配置文件和模拟 bean,并使用它们创建目标 bean。假设您使用的是基于构造函数的注入:

    @Profile("test")
    @Configuration
    public class BookServiceTestConfiguration {
    
    
        @Bean
        public BookRepository bookRepository() {
            return Mockito.mock(BookRepository.class);
        }
    
        @Bean
        public LibraryService libraryService() {
            return Mockito.mock(LibraryService.class);
        }
    
        @Bean
        public BookService bookService() {
           BookServiceImpl bookService = new BookServiceImpl(
                    bookRepository(), libraryService()
            );
    
           return userGroupService;
        }
    }
    

然后将你的测试class写成:

    @ActiveProfiles("test")
    @Import(BookServiceTestConfiguration .class)
    @RunWith(SpringRunner.class)
    public class BookServiceUnitTest {

        @Autowired
        private BookService bookService;

        // write unit tests
    }
  1. 有关更多信息,请阅读 this article

您可以通过四个步骤对其进行单元测试:

  • 删除 @SpringBootTest 注释,因为它正在旋转整个 Spring 上下文——对所有协作者都被模拟的单元测试没有用
  • BookServiceImpl 声明中删除 @Autowired 注释并添加一个 @BeforeEach 设置方法,在其中初始化 bookService 传递 bookRepositorylibraryService 作为参数
  • 在 ExtendWith 注释中使用 MockitoExtension 而不是 SpringExtension。在这里,我假设您能够使用像 Mockito 这样的库来模拟您的合作者
  • 使用 Mockito 的 @Mock 而不是 @MockBean,因为我们是手动初始化 bookService 所以不需要处理 Spring 个 beans

关于您的第一个问题再补充一点:@Mockbean@Autowired 是对集成测试有意义的注释,因为它们处理 bean 的模拟和注入。单元测试应单独考虑此 class,模拟与其他 class 的交互,因此无需启动应用程序上下文和设置 bean。