Spring Boot 测试中的 MockBean 注解导致 NoUniqueBeanDefinitionException

Posted

技术标签:

【中文标题】Spring Boot 测试中的 MockBean 注解导致 NoUniqueBeanDefinitionException【英文标题】:MockBean annotation in Spring Boot test causes NoUniqueBeanDefinitionException 【发布时间】:2017-01-18 18:55:21 【问题描述】:

我在使用 @MockBean 注释时遇到问题。文档说 MockBean 可以替换上下文中的 bean,但我在单元测试中得到了 NoUniqueBeanDefinitionException。我看不到如何使用注释。如果我可以模拟 repo,那么显然会有不止一个 b​​ean 定义。

我正在关注此处找到的示例:https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

我有一个 mongo 存储库:

public interface MyMongoRepository extends MongoRepository<MyDTO, String>

   MyDTO findById(String id);

还有泽西岛资源:

@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();
    

还有一个 JUnit 测试:

@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);

    


错误信息:

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

【问题讨论】:

【参考方案1】:

我在spring-boot 2.3.9 中遇到了同样的“问题”,但这不是错误,而是 bean 的配置问题。

至少,有两种方法可以解决:

在@MockBean注解中设置name参数:

在测试中,给MockBean添加一个name

@MockBean(name="myRepository")
private MyRepository diffrentName;

在生产代码库中使用 myRepository 作为文件名:

@Autowired
private MyRepository myRepository;

@MockBean 的名称必须与字段的名称相同。

将 MockBean 文件命名为与代码中的依赖项相同。

在测试中,使用正确的 MockBean 文件名:

@MockBean
private MyRepository customRepository;

在生产代码库中使用customRepository 作为文件名:

@Autowired
private MyRepository customRepository;

通过这种方式,您指明要使用哪个 bean。

我希望这对某人有所帮助。

【讨论】:

谢谢,这很有帮助!但是,如果我在 prod 和 test 中具有相同的名称,并且名称与类型相同,则它不起作用。因此,如果我在 prod 代码库和测试中都有 XyzRepository xyzRepository,它就会抱怨。【参考方案2】:

只需在 POM.xml 中添加以下内容

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

【讨论】:

【参考方案3】:

这是一个错误:https://github.com/spring-projects/spring-boot/issues/6541

修复在 spring-data 1.0.2-SNAPSHOT2.0.3-SNAPSHOT 中:https://github.com/arangodb/spring-data/issues/14#issuecomment-374141173

如果您不使用这些版本,您可以通过使用其名称声明模拟来解决它:

@MockBean(name="myMongoRepository")
private MyMongoRepository repository;

回应您的评论

来自Spring's doc:

为方便起见,需要对开始的 REST 调用的测试 服务器还可以 @Autowire 一个 TestRestTemplate 这将 解析到正在运行的服务器的相对链接。

读到这里,我觉得你需要用网络环境声明@SpringBootTest

@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)

如果你的spring boot没有启动web环境,那还需要TestRestTemplate。因此,我猜 spring 甚至没有提供它。

【讨论】:

谢谢。我试过了,现在 Spring 无法自动装配 TestRestTemplate:org.springframework.beans.factory.UnsatisfiedDependencyException:创建名称为“path.to.my.tests.TestMocks”的 bean 时出错:通过字段“restTemplate”表示不满足的依赖关系:没有限定为依赖项 [org.springframework.boot.test.web.client.TestRestTemplate] 找到类型为 [org.springframework.boot.test.web.client.TestRestTemplate] 的 bean:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者. 啊,是的,我已经删除了 webenvironment 的东西并忘记了。我重新添加了它,现在我收到一个关于我的 DTO 类没有合适的 HttpMessageConverter 的错误。所以看起来我已经摆脱了最初的错误,开始学习 HttpMessageConverters。谢谢! 您使用的是哪个 spring-boot 版本?

以上是关于Spring Boot 测试中的 MockBean 注解导致 NoUniqueBeanDefinitionException的主要内容,如果未能解决你的问题,请参考以下文章

使用Spring Boot的@MockBean时创建严格的模拟?

为啥弹簧测试失败,不起作用@MockBean

@MockBean 不适用于带有 JUnit 5 和 Spring Boot 2 的 @WebMvcTest?

spring单元测试Mock,MockBean踩坑及没有真实执行的理解

SpringJunitTest

[@ MockBean和@Autowired在一个测试类中使用同一服务