SpringBoot @MockBean 和 @WebMvcTest 不起作用

Posted

技术标签:

【中文标题】SpringBoot @MockBean 和 @WebMvcTest 不起作用【英文标题】:SpringBoot @MockBean and @WebMvcTest does not work 【发布时间】:2019-04-28 13:33:15 【问题描述】:

Controller.java

@RestController
public class Controller 

    @Autowired
    private UserService userService;

    @Autowired
    private BookService bookService;

    //Below userService implementation
    @PostMapping(value = "/addNewUser", consumes = "application/json")
    public void addNewUser(@RequestBody User newUser) 
        userService.addNewUser(newUser);
    

    @GetMapping(value = "/findUserById/id", produces = "application/json")
    public User findUserById(@PathVariable("id") Long id) 
        return userService.findUserById(id);
    

    @GetMapping(value = "/findUserByName/name", produces = "application/json")
    public User findUserByName(@PathVariable("name") String name) 
        return userService.findUserByName(name);
    

UserServiceImpl.java

@Transactional
@Service("userService")
public class UserServiceImpl implements UserService 

    @Autowired
    private UserRepository userRepository;

    @Override
    public void addNewUser(User newUser) 
        userRepository.save(newUser);
    

Repository.java

public interface UserRepository extends CrudRepository<User, Long> 

    Optional<User> findUserByName(String name);

    Optional<User>  findUserBySurname(String surname);

ControllerTest.java

//@RunWith(SpringRunner.class)
@RunWith(MockitoJUnitRunner.class)
@WebMvcTest(Controller.class)
public class ControllerTest 

//    @MockBean
//    private UserService userService;
//
//    @MockBean
//    private BookService bookService;

    @Mock
    private UserService userService;

    @Mock
    private BookService bookService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test()

    

上面的配置可以,但下面的不行。

@RunWith(SpringRunner.class)
//@RunWith(MockitoJUnitRunner.class)
@WebMvcTest(Controller.class)
public class ControllerTest 

    @MockBean
    private UserService userService;

    @MockBean
    private BookService bookService;

//    @Mock
//    private UserService userService;
//
//    @Mock
//    private BookService bookService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test()

    

我得到了这个错误

应用程序启动失败


说明:

一个组件需要一个名为“entityManagerFactory”的bean,它可以 找不到。

行动:

考虑在你的 配置。

2018-11-26 21:58:32.343 错误 7385 --- [主要] o.s.test.context.TestContextManager : 捕获异常同时 允许 TestExecutionListener [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@5c7bfdc1] 准备测试实例 [com.webservices.example.librarydatabaseservice.controller.ControllerTest@74f827ad]

java.lang.IllegalStateException: 无法加载 ApplicationContext 在 org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.postProcessFields(MockitoTestExecutionListener.java:99) ~[spring-boot-test-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.injectFields(MockitoTestExecutionListener.java:79) ~[spring-boot-test-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.prepareTestInstance(MockitoTestExecutionListener.java:54) ~[spring-boot-test-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) ~[junit-4.12.jar:4.12] 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) ~[junit-4.12.jar:4.12] 在 org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) ~[junit-4.12.jar:4.12] 在 org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) ~[junit-4.12.jar:4.12] 在 org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) ~[junit-4.12.jar:4.12] 在 org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) ~[junit-4.12.jar:4.12] 在 org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.junit.runners.ParentRunner.run(ParentRunner.java:363) ~[junit-4.12.jar:4.12] 在 org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.junit.runner.JUnitCore.run(JUnitCore.java:137) ~[junit-4.12.jar:4.12] 在 com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) ~[junit-rt.jar:na] 在 com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) ~[junit-rt.jar:na] 在 com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) ~[junit-rt.jar:na] 在 com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) ~[junit-rt.jar:na] 原因: org.springframework.beans.factory.BeanCreationException:错误 创建名为“bookRepository”的bean:无法创建内部bean '(内部 bean)#3f362135' 类型 [org.springframework.orm.jpa.SharedEntityManagerCreator] 设置时 bean 属性 'entityManager';嵌套异常是 org.springframework.beans.factory.BeanCreationException:错误 创建名为“(内部 bean)#3f362135”的 bean:无法解析 在设置构造函数时引用 bean 'entityManagerFactory' 争论;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 名为“entityManagerFactory”的 bean 可在 org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:327) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:131) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1648) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1400) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:575) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:827) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863) ~[spring-context-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) ~[spring-context-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) ~[spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:316) ~[spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127) ~[spring-boot-test-2.1.0.RELEASE.jar:2.1.0.RELEASE] 在 org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117) ~[spring-test-5.1.2.RELEASE.jar:5.1.2.RELEASE] ... 25个常用框架 省略原因: org.springframework.beans.factory.BeanCreationException:错误 创建名为“(内部 bean)#3f362135”的 bean:无法解析 在设置构造函数时引用 bean 'entityManagerFactory' 争论;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 名为“entityManagerFactory”的 bean 可在 org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:660) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:478) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:312) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] ... 43个常用框架 省略原因: org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 名为“entityManagerFactory”的 bean 可在 org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:772) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1212) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE] ... 51个常用框架 省略

它说 entityFactoryManager 有问题,但它以一种模拟方式工作,而另一种则没有......

完整代码Github repo

我正在使用 SpringBoot 版本:2.1.0.RELEASE

【问题讨论】:

要将 Controller 用于 ControllerTest,请尝试使用 @InjectMocks 然后在使用 MockMvc.perform(...) 之前尝试使用 when(repository.add(any())) 学习存储库方法.thenReturn(示例);某某,这清楚还是你想要一个例子? 【参考方案1】:

您的测试正在使用 entityManagerFactory bean 加载 JPA。在您的主应用程序类中,我认为您已经编写了@EnableJpaRepositories,如果您的存储库位于主应用程序的基本包下,则不需要它。删除此注释,因为您的测试类应仅用于测试 Web 层。

【讨论】:

这解决了我的问题。我花了将近一天的时间寻找答案。谢谢【参考方案2】:

尝试像这样向项目中添加配置类:

@Configuration
@EnableJpaRepositories(basePackages = "com.webservices.example.librarydatabaseservice.repository")
public class RepositoryConfiguration 

问题与您的测试无关。这是 Spring 启动和加载服务的问题。在测试的 Mockito 版本中,您不需要它加载所有类并模拟服务/存储库,这样可以掩盖问题。在另一个版本的测试中,SpringRunner 需要加载您的 applicationContext,由于缺少 JPA 配置而失败。

【讨论】:

我想避免加载 spring 上下文,所以我没有使用 SprintBootTest。我想用模拟创建快速控制器测试,但正如你所说,我必须在 SpringRunner.class 中将此配置添加到弹簧上下文加载中,所以也许更好的选择是使用 SprintBootTest 并且不创建任何配置文件。跨度>

以上是关于SpringBoot @MockBean 和 @WebMvcTest 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

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

使用 @MockBean 的 Spring Boot 集成测试不释放 jdbc 连接

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

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

在应用程序启动之前配置@MockBean 组件

为啥@MockBean ConversionService 会导致错误