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,那么显然会有不止一个 bean 定义。
我正在关注此处找到的示例: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-SNAPSHOT
和 2.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 不适用于带有 JUnit 5 和 Spring Boot 2 的 @WebMvcTest?