2

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.

  • Can't you have a list of dummy data for the tests, which you can define in @BeforEach? – Bhaumik Thakkar Feb 19 '20 at 11:43
  • I don't think spring boot will configure an HSQL database unless you define it explicitly via. both dependency and `application.properties`? Check if your application has HSQL as its dependency. And again, even if you are using HSQL, you still needs boots autoconfiguration to set you up with HSQL, which you obviously excluded. – Alanpatchi Feb 19 '20 at 12:37
  • You're right, HSQL was enabled because I had enabled it in the maven pom.xml. But unless I configure a datasource, spring boot fails with "Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.". There seems to be no way to completely disable the database yet keep my CrudRepository intact for testing. – Jonathan Jogenfors Feb 19 '20 at 19:51

1 Answers1

1

@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.

Eugene Kortov
  • 445
  • 6
  • 17
  • Thanks for your suggestion, I will read more about @TestConfiguration. However, your example does not compile: "Cannot make a static reference to the non-static field userRepository" so I can't try it directly. – Jonathan Jogenfors Feb 19 '20 at 12:19
  • @JonathanJogenfors Yep, I missed that. Actually, You can either remove `@SpringBootTest` from your code and do not use `@TestConfiguration` at all. I think about static and can't figure out yet how to solve it right (you can make `userRepository` static, but I think it can break your tests behaviour) – Eugene Kortov Feb 19 '20 at 12:28
  • No worries. I got it to work in my edit to your comment. Now on to do mocking! My only problem is there is no master off switch to say "don't touch any database", for instance I had to rename my main schema.sql in the main folder to schema-mysql.sql so that the hsql database doesn't try loading a mysl schema (and fail). Still, I can live with this. – Jonathan Jogenfors Feb 19 '20 at 12:38
  • @JonathanJogenfors glad I helped you! You can provide different test environments with "Spring profiles". https://www.baeldung.com/spring-profiles . For different profiles use different properties with profile in name. Also you can acivate make different `@bean` with `@Conditional`. – Eugene Kortov Feb 19 '20 at 12:51
  • Also you can create abstract class for your similar tests and implement it for different cases providing needed annotations or configs – Eugene Kortov Feb 19 '20 at 12:52
  • Thanks. I know about profiles. I just haven't found a way to tell Spring (even with a profile) to not use hsql or any other database. Do you know a way other than excluding autoconfigurations? It's not strictly needed, but I find it would be cleaner code. – Jonathan Jogenfors Feb 19 '20 at 12:58
  • @JonathanJogenfors no I guess I don't know. But you can try to create general properties without any database properties and then use different profiles or imports for properties – Eugene Kortov Feb 19 '20 at 13:09
  • Thanks. Just a short FYI: Spring Boot automatically uses HSQL when no database properties exist. – Jonathan Jogenfors Feb 19 '20 at 14:52
  • @JonathanJogenfors hmm I really doubt that. I didn't find such info. Can you give a link? I guess Spring boot inits db beans on classpath info via `@Conditional` annotation. So as I understand it if you include a db dependency or a starter in pom, Springboot inits such db. And you can add profile condition for your databases in pom and enable certain db with profiles – Eugene Kortov Feb 19 '20 at 14:59
  • That's how starters work https://www.baeldung.com/spring-boot-custom-starter – Eugene Kortov Feb 19 '20 at 15:15
  • Youre right, sorry. I had included h2sql in the maven xml. Without it, and with no db configuration in the properties file I get "Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured." as expected. (I tried but can't move this discussion to chat because of low karma) – Jonathan Jogenfors Feb 19 '20 at 19:49