Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MockBean annotation in Spring Boot test causes NoUniqueBeanDefinitionException

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
like image 964
JCN Avatar asked Sep 10 '16 23:09

JCN


People also ask

What is MockBean annotation?

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.

What is difference between @mock and @MockBean?

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.

Does @SpringBootTest include @MockBean?

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.

What does SpringBootTest annotation do?

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.


2 Answers

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.

like image 108
Łukasz Olszewski Avatar answered Sep 29 '22 03:09

Łukasz Olszewski


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.

like image 27
alexbt Avatar answered Sep 29 '22 04:09

alexbt