2

Im having a Springboot project where I have found a way to create and run simple Junit testcase which looks into a repository and fetches some data attribute for a given entity. The result of the Junit run is pass so no problem in regards to that.

But the thing is here, that I have seen a lot of examples out there where tutorials are showing Springboot projects where they can simply run Junit tests with only @Runwith or @SpringBootTest for their specific test classes.

In my case I have to add 3 annotations, @SpringBootTest, @RunWith as well as @ContextConfiguation(with parameters) until Im able to run the testcase.

So my question is how will I be able to run it as minimalistic as possible, (some exercises I have seen have only one annotation for their springboot test class)

My Springboot test class looks like this:

Screenshot of my Junit class

and my Directory structure looks like this:

Screenshot of my Project directory structure

My application.properties looks like this:

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/erfan
spring.datasource.username=erfan
spring.datasource.password=

#Some additional properties is trying to be set by Spring framework so this must be set
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true


#spring.datasource.initialization-mode=always
#spring.datasource.initialize=true
#spring.datasource.schema=classpath:/schema.sql
#spring.datasource.continue-on-error=true 

#HikariCP is a ConnectionPool manager, related to DB stuff



#Below is the property key you need to set to * as value to expose all kind of monitoring related information
#about your web application 

#management.endpoints.web.exposure.include=*

And my pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
    </parent>
    <groupId>com.sample</groupId>
    <artifactId>postgres</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>postgres</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>



    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>



        </plugins>






    </build>
</project>

So am I missing like something in my application.properties file? Something that I should include to be able to remove "boilerplate" annotation in my test class?

Oleksii Valuiskyi
  • 2,691
  • 1
  • 8
  • 22
Erfan Tavoosi
  • 389
  • 5
  • 16
  • To be precise: if your test involves looking into an actual repository it most certainly is not a unit test. – Amadán Feb 07 '20 at 12:26
  • @Amadán I agree. I just happened to be curious about the reason why my application was not able to run in the way I expected it – Erfan Tavoosi Feb 07 '20 at 16:13

3 Answers3

2

Depends on what you're trying to do. Basically spring has custom annotations that configures the spring context to include only relevant beans. This is the so called test slices.

But there are a few "rules" I always try to follow:

  • Avoid @SpringBootTest unless you're doing integration testing, or manually setting which classes to use @SpringBootTest(classes = {MyService1.class, MyService2.class}
  • If you're testing spring jpa, you can use the @DataJpaTest annotation, example here
  • If you're testing controllers you can use the @WebMvcTest, example here
  • If you're testing other services, you can always use @ContextConfiguration to configure the spring context accordingly.

So for example, for your test I would write it in one of two ways:

@RunWith(SpringRunner.class)
@DataJpaTest
@Import(AnotherConfig.class)
class MyTest {
   // test stuff here
}
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {AnotherConfig.class})
// NOTE, if you have a JpaConfig class where you @EnableJpaRepositories
// you can instead add this config class to the @ContextConfiguration classes
@EnableJpaRepositories
class MyTest {
   // test stuff here
}

Basically, don't worry about how many annotations you have on top of your test, but worry about which beans/services are being autowired. For example the @SpringBootTest is a single annotation, but autowires all the beans in the spring context.

Ahmed Sayed
  • 1,429
  • 12
  • 12
  • Thx a lot. I also figured it out by now that it really doesnt matter how many annotations you have. It just makes your code look bad and have no fullfilling functions. What I have come to find out is that Springboot needs declaration of packages for components and beans in a certain order in order to make the application behave the way you expect it to. Otherwise it will fail. – Erfan Tavoosi Feb 07 '20 at 18:25
0

I strongly recommend not using a bunch of spring annotations on unit tests. Unit tests should only test one piece of code and not relate with externals or other layers, so Mockito should be sufficient.

Example:

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
   @InjectMocks
   private FooService service;

   @Mock
   private FooRepository repository;

   @Test
   public void whenHappyPath_shouldReturnTrue(){
      doReturn(Optional.empty()).when(repository).findById(anyLong());
      assertTrue(service.isFoo(1L));
   }
}

You are preventing your unit test to reach the repository layer, so you don't need to create a context with embedded DB or any other thing.

If you are using for integration tests then it is different and you will need different strategies. For that, I'd recommend use embedded DB on tests (which is made by default if you have h2 dependency):

<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

And also use a integration or test spring profile:

@ActiveProfile("test") // or integration, you choose
public class FooIntegrationTest{
   ...
}

or force other configuration file to point to another configuration

@TestPropertySource(properties = { "spring.config.location=classpath:application-test.yml" })

application-test.properties

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
Eduardo Meneses
  • 504
  • 3
  • 18
  • Thank you very much. Since Im new to Springboot Im still somehow in a confusion state of where to find stuff and how to configure things so my questions might sound stupid (sorry for that) but Im in my exploration phase. Anyhow what I have come to find out is something that points to some of your point, i.e. that Springboot is sensitive when it comes to where you create packages etc so you cant expect to run it the same way some applications are showing since they have a different layout structure. – Erfan Tavoosi Feb 07 '20 at 18:20
0

Erfan, it completely depends on your test scenario.

First Scenario : Complete test (Integration Test)

If you want to test the whole of your app, like testing the Service layer, Repository layer, and the Controller layer, you need a real spring-context, so you must use all @SpringBootTest and @RunWith and ... to initialize spring context to test whole layers. (This called integration-test)

Unit Test vs Integration Test: What's the Difference

how-to-use-java-integration-testing

Second Scenario: Unit test

If you want just to test a piece of your code, just like you want to test just service layer and other layers (like repository) does not important in your scenario, in this situation you must use some new framework like Mockito, to mock the pieces that you don't want to test them, in these scenarios you don't need the **spring-context initialization ** so you don't need to use @SpringBootTest or other annotations.

Mockito Sample

So based on your scenario you can use those annotations.


I strongly recommend you to read the below link for further information about best practices for testing in java.

Modern Best Practices for Testing in Java

Community
  • 1
  • 1
  • Thank you so much. I think I have figured it out somehow. Springboot seems to be sensitive in where you put stuff (create packages and declare components and beans ) so if you dont get it the way Springboot is built then you wont be able to run it as you expect it to. – Erfan Tavoosi Feb 07 '20 at 18:22