@MockBean Example in Spring Test

By Arvind Rai, April 20, 2021
On this page we will learn using @MockBean annotation in Spring Boot unit test cases. Let us understand @MockBean point-by-point.
1. The @MockBean is a Spring Boot test annotation that is used to add mocks to ApplicationContext.
2. A mock will replace existing bean of the same type defined in the context and if no existing bean then new one will be added to context.
3. The @MockBean can be used at field level and class level in unit test classes. The @MockBean can also be used in @Configuration classes.
4. Mocks can be registered by type or bean name.
5. If a registered bean in application context is mocked then injection of this bean on field is also mocked.
6. The test class using @MockBean, is annotated with
@RunWith(SpringRunner.class) 
Or
@ExtendWith(SpringExtension.class) 

7. The @MockBean has following attributes.
answer: The org.mockito.Answers type to use on the mock.
classes: Classes to mock.
extraInterfaces: Extra interfaces to be declared on the mock.
name: Name of the bean to register.
reset: The MockReset mode.
serializable: Boolean if generated mock is serializable.
value: Alias of classes i.e. the classes to mock.

Technologies Used

Find the technologies being used in our example.
1. Java 14
2. Spring 5.3.6
3. Spring Boot 2.4.5
4. JUnit 5.7.1
5. Mockito 3.6.28
6. Maven 3.8.1

Maven Dependencies

Find the Maven dependencies.
pom.xml
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.4.5</version>
</parent>
<properties>
	<context.path>spring-app</context.path>
	<java.version>14</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.7.1</version>
		<scope>test</scope>
	</dependency>	
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-engine</artifactId>
		<version>5.7.1</version>
		<scope>test</scope>
	</dependency>	
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-params</artifactId>
		<version>5.7.1</version>
		<scope>test</scope>
	</dependency>		    
	<dependency>
		<groupId>org.junit.platform</groupId>
		<artifactId>junit-platform-launcher</artifactId>
		<version>1.7.1</version>
		<scope>test</scope>
	</dependency>	    	    		
</dependencies> 

Mock at Field Level

Here we will use @MockBean at field level. In this case @Autowired will not be annotated for dependency injection.
MyAppTest1.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.concretepage.config.AppConfig;
import com.concretepage.service.MyService1;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyAppTest1 {
    @MockBean   
    private MyService1 myService;
    @Test
    public void testApp1() {
        Mockito.when(myService.getMessage()).thenReturn("Welcome");
        String msg = myService.getMessage();
        assertEquals("Welcome", msg);
    }	 
} 
We can see that getMessage() method of MyService1 class has been mocked.
If a service class is using a bean which has been mocked, then that service will receive mocked bean in our test class.
MyAppTest1.java
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyAppTest1 {
    @MockBean   
    private MyService1 myService1;
    
    @MockBean   
    private MyService2 myService2;   
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testApp1() {
        Mockito.when(myService1.getMessage()).thenReturn("Welcome");
        String msg = myService1.getMessage();
        assertEquals("Welcome", msg);
    }	 
    
    @Test
    public void testApp2() {
        Mockito.when(myService2.getCount()).thenReturn(100);
        int count = userService.getUserCount();
        assertEquals(100, count);
    }    
} 
In the second test method, the getCount() method of MyService2 has been mocked to return 100. The getCount() is being called by getUserCount() of UserService. In the test result, we can see that getUserCount() will call mocked getCount() method.
Find the classes used in our unit test class.
MyService1.java
@Service
public class MyService1 {
    public String getMessage() {
    	return "Hello World!";
    }
} 
MyService2.java
@Service
public class MyService2 {
    public int getCount() {
    	return 50;
    }
} 
UserService.java
@Service
public class UserService {
  @Autowired
  MyService2 myService;
  
  public int getUserCount() {
	return myService.getCount();
  }
} 

Mock at Class Level

The @MockBean has attributes classes and value. The value is the alias of classes. We annotate test class with @MockBean and configure classes to it.
More than one classes at class level, are configured in following ways.
1. Using value attribute.
@ExtendWith(SpringExtension.class)
@MockBean({MyService1.class, MyService2.class})
public class MyAppTest2 {
------
} 
2. Using repeated @MockBean.
@ExtendWith(SpringExtension.class)
@MockBean(MyService1.class)
@MockBean(MyService2.class)
public class MyAppTest2 {
------
} 
3. Using @MockBeans. The @MockBeans is the container annotation that aggregates several @MockBean annotations.
@ExtendWith(SpringExtension.class)
@MockBeans({
  @MockBean(MyService1.class),
  @MockBean(MyService2.class)
})
public class MyAppTest2 {
------
} 

Find a complete test class with @MockBean annotated at class level.
MyAppTest2.java
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
@MockBeans({
  @MockBean(MyService1.class),
  @MockBean(MyService2.class)
})
public class MyAppTest2 {
    @Autowired
    private MyService1 myService1;
    
    @Autowired  
    private MyService2 myService2;    
    
    @Test
    public void testApp1() {
        Mockito.when(myService1.getMessage()).thenReturn("Welcome");
        String msg = myService1.getMessage();
        assertEquals("Welcome", msg);
    }	 
    
    @Test
    public void testApp2() {
        Mockito.when(myService2.getCount()).thenReturn(100);
        int count = myService2.getCount();
        assertEquals(100, count);
    }    
} 

Mock using @Configuration

The classes can be mocked in a @Configuration annotated class using @MockBean.
MyAppTest3.java
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppTestConfig.class)
public class MyAppTest3 {
    @Autowired
    private MyService1 myService1;
    
    @Autowired  
    private MyService2 myService2;    
    
    @Test
    public void testApp1() {
        Mockito.when(myService1.getMessage()).thenReturn("Welcome");
        String msg = myService1.getMessage();
        assertEquals("Welcome", msg);
    }	 
    
    @Test
    public void testApp2() {
        Mockito.when(myService2.getCount()).thenReturn(100);
        int count = myService2.getCount();
        assertEquals(100, count);
    }    
}

@Configuration
class AppTestConfig {
  @MockBean
  private MyService1 myService1;
  
  @MockBean  
  private MyService2 myService2;  
} 
We can see that two service classes have been mocked in configuration class. In our test class the mocked classes will be injected using @Autowired annotation.

@MockBean with @Qualifier

If an interface has more than one implementation class then to choose the exact class for dependency injection, @Qualifier annotation is used. Here we will show the demo to use @MockBean with @Qualifier annotation.
MyAppTest4.java
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyAppTest4 {
  @MockBean
  @Qualifier("deer")
  private Animal animal1;
  
  @MockBean
  @Qualifier("fox")
  private Animal animal2;

  @Test
  public void testApp1() {
	Mockito.when(animal1.getName()).thenReturn("xxx");
	assertEquals("xxx", animal1.getName());
  }

  @Test
  public void testApp2() {
	Mockito.when(animal2.getName()).thenReturn("yyy");
	assertEquals("yyy", animal2.getName());
  }
} 

Find the classes used in our unit test class.
Animal.java
public interface Animal {
  String getName();
} 
Deer.java
@Component("deer")
public class Deer implements Animal {
  @Override
  public String getName() {
	return "Deer";
  }
} 
Fox.java
@Component("fox")
public class Fox implements Animal {
  @Override
  public String getName() {
	return "Fox";
  }
} 
Find the print screen of the output.
@MockBean Example in Spring Test

References

Annotation Type MockBean
Annotation Type MockBeans

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us