如何在使用Spring Boot @WebMvcTest进行测试时排除其他@Controller的上下文。

6
我有多个控制器,我理解@WebMvcTest中指定一个控制器后其他控制器不会被加载到上下文中。关于此,请参考文档

controllers - 指定要测试的控制器。如果所有@Controller bean都应添加到应用程序上下文中,则可以留空。

我的第一个控制器

@Controller
public class MyController {

    @Autowired
    private MyService myService;

    private final Logger logger = Logger.getLogger(this.getClass());

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public @ResponseBody ResponseEntity<String> index() {
        try {
            myService.get();
            return new ResponseEntity<String>(HttpStatus.OK);
        } catch (Exception e) {
            logger.error(e);
            e.printStackTrace();
        }
        return new ResponseEntity<String>("REQUEST FAILED", HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

我的另一个控制器

@Controller
public class MyOtherController {

    @Autowired
    private MyOtherService myOtherService;

    etc...
}

我的控制器测试

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = { MyController.class }, secure = false)
@ActiveProfiles({ "test" })
public class MyControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    MyService myService;

    @Test
    public void testBaseReq() throws Exception {
        Testing dummyData = new Testing();
        dummyData.setData("testing");
        when(myService.get(anyInt())).thenReturn(dummyData);

        this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk());
    }
}

但是当我运行这个测试时,在加载上下文时,它尝试从MyOtherContoller中加载bean MyOtherService而失败。

2017-09-28 11:50:11.687 DEBUG 16552 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'myOtherController'
2017-09-28 11:50:11.687 DEBUG 16552 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'myOtherController'
2017-09-28 11:50:11.687 DEBUG 16552 --- [           main] o.s.b.f.annotation.InjectionMetadata     : Registered injected element on class [my.package.other.myOtherController]: AutowiredFieldElement for private my.package.other.myOtherService my.package.other.myOtherController.myOtherService
2017-09-28 11:50:11.687 DEBUG 16552 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'myOtherController' to allow for resolving potential circular references
2017-09-28 11:50:11.687 DEBUG 16552 --- [           main] o.s.b.f.annotation.InjectionMetadata     : Processing injected element of bean 'myOtherController': AutowiredFieldElement for private my.package.other.myOtherService my.package.other.myOtherController.myOtherService
2017-09-28 11:50:11.688 DEBUG 16552 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'myOtherService'
2017-09-28 11:50:11.688 DEBUG 16552 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'myOtherService'
2017-09-28 11:50:11.689 DEBUG 16552 --- [           main] o.s.b.f.annotation.InjectionMetadata     : Registered injected element on class [my.package.other.myOtherService]: AutowiredFieldElement for private my.package.other.myOtherMapper my.package.other.myOtherService.myOtherMapper
2017-09-28 11:50:11.689 DEBUG 16552 --- [           main] o.s.b.f.annotation.InjectionMetadata     : Registered injected element on class [my.package.other.myOtherService]: AutowiredFieldElement for private ie.aib.services.coredemo.FinancialsRegionService my.package.other.myOtherService.financialsRegionService
2017-09-28 11:50:11.689 DEBUG 16552 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'myOtherService' to allow for resolving potential circular references
2017-09-28 11:50:11.689 DEBUG 16552 --- [           main] o.s.b.f.annotation.InjectionMetadata     : Processing injected element of bean 'myOtherService': AutowiredFieldElement for private my.package.other.myOtherMapper my.package.other.myOtherService.myOtherMapper
2017-09-28 11:50:11.690  WARN 16552 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myOtherController': Unsatisfied dependency expressed through field 'myOtherService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myOtherService': Unsatisfied dependency expressed through field 'myOtherMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'my.package.other.myOtherMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

我以为在WebMvcTest注解中指定要测试的控制器会限制它只加载该控制器。但它尝试加载其他控制器,因为它们的bean没有被mock,所以失败了。

我错了吗?还是我理解有误?我认为我只需要mock正在测试的控制器的beans。我也尝试过使用excludeFilter来特别排除其他控制器的包,但这并没有改变错误。


你的应用程序类是什么样子的? - Darren Forsythe
2个回答

9

请确保您的测试选择的Application.class没有包含@ComponentScan注释。例如,这是您的包结构

abc-project
  +--pom.xml
  +--src
    +-- main
      +-- com
        +-- abc
          +-- Application.java
          +-- controller
            +-- MyController.java
    +-- test
      +-- com
        +-- abc
          +-- Application.java
          +-- controller
            +-- MyControllerTest.java

测试中的 Application.java 应该类似于以下示例:
@SpringBootApplication
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

谢谢@Indra,我想那就是我的问题。我有一个@ComponentScan,因为我需要显式过滤以排除包含在已包含的jar中的某些组件。现在我正在寻找更改,以便在运行WebMvcTests时拥有单独的应用程序类。 - MadMurf
谢谢,你让我的一天变得美好了。我之前遇到了一个不同的错误(Spring试图在上下文中实例化一个不应该在测试中实例化的bean),而你的建议对我也起了作用。 - Antonio
1
我遇到了同样的问题,但不幸的是,这个解决方案对我不起作用。如果我删除@ComponentScan@SpringBootApplication注释将随之消失,每个测试都会出现404错误。 - Ivan Vilanculo
@IvanVilanculo 如果我没有看到你的项目结构,就很难对其进行评论。 - Indra Basak

0

my.package.other.myOtherMapper(可能是Mybatis Mapper文件)丢失或未正确初始化。

据我所知,myOtherService实现类具有Mapper文件,但未正确映射。

您可能需要先映射它们。如果可能的话,您可以发布Mapper xml内容。

  <context:component-scan base-package="org.example">       
    <context:exclude-filter type="custom" expression="abc.xyz.MyOtherController"/>
  </context:component-scan>

是的,myOtherMapper没有被初始化,但它在试图实例化它,因为它正在尝试将MyOtherController加载到上下文中。如果该控制器从上下文中排除掉,我认为应该这样做,那么就不会查找myOtherService,因此也就无法查找myOtherMapper了。 - MadMurf
我可以通过在MyOtherService的测试中添加@MockBean来解决这个问题,但是我不应该为了测试仅使用一个服务的控制器而模拟每个服务。 - MadMurf
试一下这个https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-scanning-filters - Raja CSP
谢谢,但我已经尝试使用排除过滤器来忽略其他控制器的包,但没有任何区别。 - MadMurf
但是,如果我必须明确排除每个其他的类都是控制器,那么随着功能的增加,我将永远修改我不相关的单元测试。问题是为什么Spring Boot上下文在加载其他控制器时,WebMvcTest文档似乎表明它不应该这样做。 - MadMurf
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接