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

Mock class inside REST controller with Mockito

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

@RestController
public class Controller {

  @Autowired
  private Processor processor;

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

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

  }   
}

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

我正在尝试使用 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)
    ...

有人可以给我提示吗,这个 mock 如何注入我的控制器?

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

@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?

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

...

@Autowired
private Controller controller;

...

@Before
public void setUp() throws Exception {

    ...

    Processor processor = Mockito.mock(Processor.class);
    ReflectionTestUtils.setField(controller, "processor", processor);
}
@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);
  }

在上面输入 "Your returned value" 的返回值,并在测试中使用此值来验证您的输出。

修改您的控制器以使用构造函数注入而不是字段注入。这使依赖关系明确,并使您的测试设置大大简化。

您可以删除 SpringApplicationConfiguration 中的 servlet 上下文 class 和模拟 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 将被模拟并将模拟注入控制器。