I am having trouble using the @MockBean annotation. The docs say MockBean can replace a bean within the context, but I am getting a NoUniqueBeanDefinitionException within my unit test. I can't see how to use the annotation. If I can mock the repo, then obviously there will be more than one bean definition.
I am following the examples found here: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
I have a mongo repository:
public interface MyMongoRepository extends MongoRepository<MyDTO, String>
{
MyDTO findById(String id);
}
And a Jersey resource:
@Component
@Path("/createMatch")
public class Create
{
@Context
UriInfo uriInfo;
@Autowired
private MyMongoRepository repository;
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response createMatch(@Context HttpServletResponse response)
{
MyDTO match = new MyDTO();
match = repository.save(match);
URI matchUri = uriInfo.getBaseUriBuilder().path(String.format("/%s/details", match.getId())).build();
return Response.created(matchUri)
.entity(new MyResponseEntity(Response.Status.CREATED, match, "Match created: " + matchUri))
.build();
}
}
And a JUnit test:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMocks {
@Autowired
private TestRestTemplate restTemplate;
@MockBean
private MyMongoRepository mockRepo;
@Before
public void setup()
{
MockitoAnnotations.initMocks(this);
given(this.mockRepo.findById("1234")).willReturn(
new MyDTO());
}
@Test
public void test()
{
this.restTemplate.getForEntity("/1234/details", MyResponseEntity.class);
}
}
Error message:
Field repository in path.to.my.resources.Create required a single bean, but 2 were found:
- myMongoRepository: defined in null
- path.to.my.MyMongoRepository#0: defined by method 'createMock' in null
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.
We can use the @MockBean to add mock objects to the Spring application context. The mock will replace any existing bean of the same type in the application context. If no bean of the same type is defined, a new one will be added.
Spring Boot provides @MockBean annotation that can be used to define a Mockito mock for a bean inside our ApplicationContext , that means the mock declared in test class (by using @MockBean) will be injected as a bean within the application.
The @SpringBootTest annotation loads the complete Spring application context. In contrast, a test slice annotation only loads beans required to test a particular layer. And because of this, we can avoid unnecessary mocking and side effects.
I had the same "issue" in spring-boot 2.3.9
but it's not a bug, it's problem with the configuration of beans.
At least, There are two ways to solve it:
Set name
parameter in @MockBean annotation:
In the test, add a name
to MockBean
:
@MockBean(name="myRepository")
private MyRepository diffrentName;
and in the production codebase use myRepository
as filed name :
@Autowired
private MyRepository myRepository;
The name of @MockBean must be the same as the name of the field.
Name a MockBean filed the same as a dependency in code.
In the test, use the correct name of MockBean filed:
@MockBean
private MyRepository customRepository;
and in the production codebase use customRepository
as filed name :
@Autowired
private MyRepository customRepository;
in that way, You indicate which bean You want to use.
I hope this will be helpful for someone.
It's a bug: https://github.com/spring-projects/spring-boot/issues/6541
The fix is in spring-data 1.0.2-SNAPSHOT
and 2.0.3-SNAPSHOT
: https://github.com/arangodb/spring-data/issues/14#issuecomment-374141173
If you aren't using these version, you can work around it by declaring the mock with its name:
@MockBean(name="myMongoRepository")
private MyMongoRepository repository;
In response to your comment
From Spring's doc:
For convenience, tests that need to make REST calls to the started server can additionally @Autowire a TestRestTemplate which will resolve relative links to the running server.
Reading this, I think you need to declare @SpringBootTest
with a web environment:
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
If your spring boot doesn't start the web environment, then what is the need for TestRestTemplate
. Thus, I guess spring does not even make it available.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With