I am developing an app that uses Spring Boot 2 and Spring Data JDBC. The final app will use MySQL.
I currently have a persistence layer and a service layer together with a few repositories derived from CrudRepository.
For my unit tests, I want to really do unit tests without any database. Not even HSQL. I know there are several different philosophies to subscribe to here, but I want to switch to pure unit tests and see how it plays out. I will use a database in the integration tests.
I have a separate application.properties
in my test folder, where I have a separate configuration for my tests.
Spring Boot will default to HSQL if no datasource is defined in application.properties
. But again, I want to run my tests without any database and only mock my CrudRepository implementations. I tried disabling Spring Boot's database autoconfiguration by using different variants of the following in my application.properties
:
spring.autoconfigure.exclude= \
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
In my tests, I tried to do something like this:
@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest
public class UserServiceTest {
private UserService userService;
@MockBean
private UserRepository userRepository;
@BeforeEach
void setUp() {
userService = new UserService(userRepository);
}
@Test
public void doSomeTest() {
// Test code goes here
}
}
I am using constructor-based dependency injection in the services. However, I get errors from Spring like the following:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'package.userRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
What I want is a mocked userRepository where I can pre-configure a faked repository to return dummy data. But it appears that removing the database configuration completely removes the CrudRepository so that no part of the spring boot app can start. Or am I missing something else.
I am using Eclipse, Maven, Mockito and JUnit5. I should note that everything works if I run "unit tests" against a test mysql database, so I know there's nothing really wrong with the code or with the tests or the setup in general. The issues start only when I want to get rid of the database.
I am a junior Java developer doing this for fun, so I might be missing something obvious. Searching around on the web for Spring Data JDBC is difficult, since I almost only find solutions relating to JPA.
@SpringBootTest
loads all your context. Spring boot – @SpringBootTest
Under the hood, @SpringBootTest tries to mimic the processes added by Spring Boot framework for creating the context e.g. it decides what to scan based on package structures, loads external configurations from predefined locations, optionally runs auto-configuration starters and so on.
As we see that this annotation starts and configure almost whole application before the test begin, we should use @SpringBootTest to write an integration tests that use the application processes and dependencies.
Including UserService
. And I guess it can't find UserRepository
so it fails.
I guess you should remove these strings
spring.autoconfigure.exclude= \
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
and test like this
@ExtendWith(SpringExtension.class)
@SpringBootTest
@Transactional
public class UserServiceTest {
@TestConfiguration
class UserServiceTestContextConfiguration {
@Bean
public UserService userService() {
return new UserService(userRepository);
}
}
@Autowired
private UserService userService;
@MockBean
private UserRepository userRepository;
// write test cases here
}
I took it from here
And keep in mind that Spring handles @TestConfiguration
specifically so don't "shot your leg" with it Spring boot – @TestConfiguration
In spring boot, any beans configured in a top-level class annotated with @TestConfiguration will not be picked up via component scanning. We must explicitly register the @TestConfiguration class with the class that contains the test cases.