使用 Mockito 在 REST 控制器中模拟类

Posted

技术标签:

【中文标题】使用 Mockito 在 REST 控制器中模拟类【英文标题】:Mock class inside REST controller with Mockito 【发布时间】:2015-09-07 23:08:34 【问题描述】:

我有一个 spring-boot 应用程序,它通过控制器公开一个 REST 接口。这是我的控制器的一个例子:

@RestController
public class Controller 

  @Autowired
  private Processor processor;

  @RequestMapping("/magic")
  public void handleRequest() 

    // process the POST request
    processor.process();

     

我正在尝试为此类编写单元测试,并且必须模拟处理器(因为处理需要很长时间,并且在测试控制器行为期间我试图避免此步骤)。请注意,为了这个问题,提供的示例已简化。

我正在尝试使用 mockito 框架来完成这项任务:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
@WebAppConfiguration
@ActiveProfiles("test")
public class ControllerTest 

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setUp() throws Exception 

    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

    Processor processor = Mockito.mock(Processor.class);
    ReflectionTestUtils.setField(Controller.class, "processor", processor);
  

  @Test
  public void testControllerEmptyBody() throws Exception 

    this.mockMvc.perform(post("/magic")).andExpect(status().isOk());

  

但是,这失败了

java.lang.IllegalArgumentException: Could not find field [processor] of type [null] on target [class org.company.Controller]
    at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:112)
    ...

请有人给我一个提示,如何将这个模拟注入到我的控制器中?

【问题讨论】:

【参考方案1】:

你不应该传递一个实例来设置字段,而不是类,例如:

...

@Autowired
private Controller controller;

...

@Before
public void setUp() throws Exception 

    ...

    Processor processor = Mockito.mock(Processor.class);
    ReflectionTestUtils.setField(controller, "processor", processor);

【讨论】:

有趣...我没有想到我也可以自动连接控制器。谢谢。像魅力一样工作!【参考方案2】:

我认为你可以像这样直接注入模拟:

@InjectMocks
private ProcessorImpl processorMock;

并删除这一行:

ReflectionTestUtils.setField(Controller.class, "processor", processor);

见Injection of a mock object into an object to be tested declared as a field in the test does not work using Mockito?

【讨论】:

尝试使用 injectMocks 注释,但它不起作用(原始处理器被调用)。甚至尝试将其命名为与控制器内的字段名称相同...【参考方案3】:

重新设计您的控制器以使用构造函数注入而不是字段注入。这使依赖关系变得明确,并使您的测试设置变得更加简单。

【讨论】:

【参考方案4】:
@Before
  public void setUp() throws Exception 

    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();

    Processor processor = Mockito.mock(Processor.class);
//This line should be added to perform mock for Processor.
   Mockito.when(processor.process()).thenReturn(<Your returned value>);
    //ReflectionTestUtils.setField(Controller.class, "processor", processor);
  

在上面将您的返回值放入“您的返回值”,并在测试中使用此值来验证您的输出。

【讨论】:

【参考方案5】:

您可以删除SpringApplicationConfiguration 中的servlet 上下文类并模拟servlet 上下文。不需要注入WebApplicationContextReflectionTestUtils

基本上,您的代码应如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
@ActiveProfiles("test")
public class ControllerTest 

  @InjectMocks
  private MyController controller;

  @Mock
  private Processor processor;

  private MockMvc mockMvc;

  @Before
  public void setUp() throws Exception 
    MockitoAnnotations.initMocks(this);
    this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

  

  @Test
  public void testControllerEmptyBody() throws Exception 
    when(proessor.process()).thenReturn(<yourValue>);    

    this.mockMvc.perform(post("/magic")).andExpect(status().isOk());

    verify(processor, times(<number of times>)).process();
  

Processor 将被模拟,模拟将被注入控制器。

【讨论】:

以上是关于使用 Mockito 在 REST 控制器中模拟类的主要内容,如果未能解决你的问题,请参考以下文章

如何模拟 JWT 令牌以将其与 Mockito 和 Spring Boot 一起使用

如何使用 Mockito 对 void 方法的行为进行编程? [复制]

Mockito:参数的模拟服务抛出空指针异常

Mockito:等待与参数匹配的调用

颤振中的 mockito 缺少存根错误。尝试在模拟的 SharedPreferences 上使用 setString

如何在 Mockito 中模拟全局变量