Reputation: 1168
I have the following test:
@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);
}
}
It is okay, and it runs, but I was wondering is there a way to run this as a Unit test instead of a Integration tests and still use @MockBean @Autowired. Or am I missing some point?
I tried leaving only @ExtendWith(SpringExtension.class), but I get an Exception about BookServiceImpl bean not found.
I know how to do it using MockitoExtension and @Mock, @InjectMocks, but I was wondering if there is a more SpringBoot way of doing it?
Upvotes: 1
Views: 820
Reputation: 750
You can make it unit test in four steps:
@SpringBootTest
annotation, as it is spinning up the entire Spring context ―not useful for unit tests where all collaborators are mocked@Autowired
annotation from BookServiceImpl declaration and add a @BeforeEach
setup method where you initialize bookService
passing bookRepository
and libraryService
as parametersMockitoExtension
instead of SpringExtension
in the ExtendWith annotation. Here I'm assuming you are able to use a library like Mockito for mocking your collaborators@Mock
instead of @MockBean
, as we are manually initializing bookService
so there is no need to deal with Spring beansTo add a bit more on your first question: @Mockbean
and @Autowired
are annotations that make sense for integration tests as they handle the mocking and injection of beans. Unit tests should consider this class in isolation, mocking interactions with other classes, so there is no need to spin up the application context and set up beans.
Upvotes: 2
Reputation: 460
Remove @SpringBootTest
, this will load whole context and slow down your tests. The role of @MockBean
is to create a mock from the specified bean and add it to the context. Since there's no context running, There's not point in using @MockBean
Annotate your unit test class with @RunWith(SpringRunner.class)
For injecting dependencies, this is a good practice to create a configuration file and mock beans explicitly and create the target bean using them. Assuming you're using constructor-based injection:
@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;
}
}
Then write your test class as:
@ActiveProfiles("test")
@Import(BookServiceTestConfiguration .class)
@RunWith(SpringRunner.class)
public class BookServiceUnitTest {
@Autowired
private BookService bookService;
// write unit tests
}
Upvotes: 0