@SpringBootTest Annotation Example

By Arvind Rai, August 26, 2023
On this page, we will learn to create test cases for our Spring Boot application using @SpringBootTest annotation.
1. The @SpringBootTest is used to perform Spring Boot based tests and has default context loader as SpringBootContextLoader. We can change the default context loader by specifying to @ContextConfiguration(loader= ...) .
2. If @SpringBootTest has not defined classes attribute and we have not used nested @Configuration in our test class, then @SpringBootTest automatically searches for @SpringBootConfiguration.
3. The @SpringBootTest has properties attribute that allows to use custom Environment properties.
4. The @SpringBootTest has args attribute that allows to define application arguments.
5. The @SpringBootTest supports the different webEnvironment modes. It can also start fully running web server listening on user defined or random port.
6. In web testing with fully running server, the @SpringBootTest registers TestRestTemplate and WebTestClient bean as needed.
7. The @SpringBootTest works only for Spring Boot application. It means our application should have a class annotated with @SpringBootApplication or @SpringBootConfiguration. We can also use @SpringBootTest with classes attribute specifying @Configuration classes.

1. Maven Dependencies

Find the Maven dependencies used in in our example.
pom.xml
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>3.1.2</version>
</parent>
<properties>
    <context.path>spring-app</context.path>
	<java.version>17</java.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
	</dependency>	
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>
    </dependency>	
</dependencies>  

2. @SpringBootTest Annotation Structure

Find the @SpringBootTest annotation structure from Spring doc.
1. Annotation Declaration :
@Target(value={ElementType.TYPE})
@Retention(value=RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(value=SpringBootTestContextBootstrapper)
@org.junit.jupiter.api.extension.ExtendWith(value={SpringExtension})
public abstract @interface SpringBootTest extends Annotation {
    ------
} 
2. Nested Classes : It has two enums as following.
(a) SpringBootTest.WebEnvironment : Values are DEFINED_PORT, MOCK, RANDOM_PORT and NONE .
(b) SpringBootTest.UseMainMethod : Values are ALWAYS, NEVER and WHEN_AVAILABLE .

3. Elements :
(a) args : Application arguments as an array of String.
(b) classes : Component classes as an array of Class.
(c) properties : Properties in the form of key=value as an array of String that should be added to the Spring Environment before test run.
(d) webEnvironment : The type of web environment to create.
(e) useMainMethod : Decides using main method when creating and running the SpringApplication under test.
(f) value : Alias for properties.

3. JUnit @ExtendWith

The @SpringBootTest is annotated with JUnit @ExtendWith as @ExtendWith(SpringExtension.class). It means JUnit is already integrated with @SpringBootTest and we can use JUnit tests in our Spring Boot test cases. The Spring SpringExtension is used to integrate Spring TestContext with JUnit Jupiter tests. The test cases written with @ExtendWith and @ContextConfiguration can be written directly using @SpringBootTest in Spring Boot tests.
Find the test code with @ExtendWith and @ContextConfiguration annotation.
package com.concretepage;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.concretepage.config.AppConfig;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {AppConfig.class})
public class MyAppTest {
	@Test
	void myTest() {
		String name = "Shri Ganesh";
		assertThat(name).isEqualTo("Shri Ganesh");
	}
} 
The above test case can be written using @SpringBootTest as following.
@SpringBootTest
public class MyAppTest {
	@Test
	void myTest() {
		String name = "Shri Ganesh";
		assertThat(name).isEqualTo("Shri Ganesh");
	}
} 
@SpringBootTest Annotation Example


4. args : Pass Arguments

In our Spring Boot application, we pass arguments using command line. We can fetch command line arguments in a component implementing CommandLineRunner. While creating the test cases, we pass the arguments using args attribute of @SpringBootTest annotation as an array of String.
@SpringBootTest(args = { "data1", "data2" }) 
Find the example.
@SpringBootTest(args = { "data1", "data2" })
public class MyAppTest {
	@Autowired
	MyCommandLineRunner runner;
	@Test
	void myTest() {
		assertThat(runner.getArgsByIndex(0)).isEqualTo("data1");
	}
} 
Find the MyCommandLineRunner component code.
@Component
public class MyCommandLineRunner implements CommandLineRunner {
    private String[] myArgs;
    public void run(String... args) {
    	this.myArgs = args;
    	String strArgs = Arrays.stream(args).collect(Collectors.joining(", "));
    	System.out.println("Application arguments: " + strArgs);
    }
    public String getArgsByIndex(int index) {
    	return this.myArgs[index];
    }
} 

5. classes : Pass Configuration Classes

The classes attribute of @SpringBootTest is to configure components and JavaConfig classes with Spring boot tests. We need to pass array of Class to classes attribute. If we have defined any configuration class, @SpringBootTest automatically detects and loads it. We can also specify them using classes attribute.
Suppose we have a class AppConfig annotated with @Configuration annotation, we pass to Spring Boot test as following.
@SpringBootTest(classes = {AppConfig.class})
public class MyAppTest {
    @Autowired
	MyService myService;
	@Test
	void myTest() {
		assertThat(myService.getMessage()).isEqualTo("Welcome");
	}
} 

6. properties : Pass Properties as key/value

Using properties attribute of @SpringBootTest annotation, we can add properties that should be added to Spring Environment before test run. The properties are added in key=value form as an array of String.
Find the example.
@SpringBootTest(properties = {"role=admin", "db=Oracle"})
public class MyAppTest {
	@Value("${role}")
	String userRole;
	@Value("${db}")
	String database;	
	@Test
	void test() {
		assertThat(userRole).isEqualTo("admin");
		assertThat(database).isEqualTo("Oracle");
	}
} 
The value attribute is the alias for properties. We can also configure properties using value attribute as following.
@SpringBootTest({"role=admin", "db=Oracle"})
public class MyAppTest {
  ------
} 


7. webEnvironment : Create Web Application Context

The webEnvironment attribute of @SpringBootTest is to create the type of web environment when applicable. The value for webEnvironment is of SpringBootTest.WebEnvironment enum type as given below.

1. DEFINED_PORT : Creates a reactive web application context without defining server.port Environment property.
2. RANDOM_PORT : Creates reactive or servlet based web application context and sets server.port Environment property listening on random port. We usually use it in conjunctional with @LocalServerPort.
3. MOCK : Creates
a. Web application context with mock servlet environment if servlet APIs are on the classpath.
b. Reactive web application context if Spring WebFlux is on the classpath.
c. Regular application context otherwise.
The MOCK web environment is default value for webEnvironment attribute.
4. NONE : Creates application context setting web application type to WebApplicationType.NONE.

Example-1:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyAppTest {
    @LocalServerPort
    private int serverPort;
    @Test
    public void myTest(){
       assertThat(serverPort > 0).isTrue();
    }
} 
Example-2:
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, properties = {"server.port=2010"})
public class MyAppTest {
    @LocalServerPort
    private int serverPort;
    @Test
    public void myTest(){
       assertThat(serverPort == 2010).isTrue();
    }
} 

8. useMainMethod : Decide using main() method

The useMainMethod attribute of @SpringBootTest decides using main method when creating and running the SpringApplication under test. Its values are SpringBootTest.UseMainMethod enum type i.e. ALWAYS, NEVER and WHEN_AVAILABLE . Default value is NEVER. For the value NEVER, main method of application does not run to create SpringApplication while running test code where as for the value ALWAYS, main method always runs.

Example : Find the main class with main method of our application. Here I am adding a key/value to environment.
@SpringBootApplication
public class MyApplication {
	public static void main(String[] args) {
		SpringApplication application = new SpringApplication(MyApplication.class);
		application.setEnvironment(null);
		Map<String, Object> map = new HashMap<>();
		map.put("role", "admin");
		application.setDefaultProperties(map);
		application.run(args);
	}
} 
Find the test case where I am using UseMainMethod.ALWAYS. I am testing the property role that I have set in main method.
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
public class MyAppTest {
    @Autowired 
    Environment environment;
    @Test
    public void myTest(){
    	assertThat(environment.getProperty("role")).isEqualTo("admin");
    }
} 
The above test will pass.
Now I am using UseMainMethod.NEVER.
@SpringBootTest(useMainMethod = UseMainMethod.NEVER)
public class MyAppTest {
    @Autowired 
    Environment environment;
    @Test
    public void myTest(){
    	assertThat(environment.getProperty("role")).isEqualTo("admin");
    }
} 
The above test will fail.
To make it success, we can set properties using properties attribute. Find the code below.
@SpringBootTest(useMainMethod = UseMainMethod.NEVER, properties = {"role=admin"})
public class MyAppTest {
    @Autowired 
    Environment environment;
    @Test
    public void myTest(){
    	assertThat(environment.getProperty("role")).isEqualTo("admin");
    }
} 
The above test will pass.

9. Reference

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE












©2024 concretepage.com | Privacy Policy | Contact Us