如何使用 power mock 对 Spring Boot Rest 控制器和异常处理程序进行单元测试

Posted

技术标签:

【中文标题】如何使用 power mock 对 Spring Boot Rest 控制器和异常处理程序进行单元测试【英文标题】:How to unit testing spring boot rest controller and exception handler using power mock 【发布时间】:2017-10-10 05:09:30 【问题描述】:

我有一个简单的 Spring 启动应用程序,它包含员工控制器,如果过去的年份大于 2014 年并且如果它不小于 2014 年,则返回员工姓名,那么我将抛出一个自定义异常并在异常处理程序中处理它. 我想使用 powermock 对异常流进行单元测试,但我不知道该怎么做。我已经浏览了一些链接,但无法理解。 目前我收到 java.lang.IllegalArgumentException: WebApplicationContext is required。

EmployeeController.java

@RestController
public class EmployeeController

    @GetMapping(value = "/employee/joiningYear",produces = MediaType.APPLICATION_JSON_VALUE)
    public List<String> getEmployeeById(@PathVariable int joiningYear) throws YearViolationException 

        if(joiningYear < 2014)
            throw new YearViolationException("year should not be less than 2014");
        else

            // send all employee's names joined in that year 
        
        return null;
    
   

异常处理程序

@RestControllerAdvice
public class GlobalControllerExceptionHandler 


    @ExceptionHandler(value =  YearViolationException.class )
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiErrorResponse yearConstraintViolationExceptio(YearViolationException ex) 

        return new ApiErrorResponse(400, 5001, ex.getMessage());
    

自定义异常

public class YearViolationException extends Exception 

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public YearViolationException(String message) 

        super(message);
    


Junit 到单元测试异常处理程序

@RunWith(PowerMockRunner.class)
@WebAppConfiguration
@SpringBootTest
public class ExceptionControllerTest 

    @Autowired
    private WebApplicationContext applicationContext;

    private MockMvc mockMVC;

    @Before
    public void setUp() 

        mockMVC = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    

    @Test
    public void testhandleBanNotNumericException() throws Exception 

        mockMVC.perform(get("/employee/2010").accept(MediaType.APPLICATION_JSON)).andDo(print())
                .andExpect(status().isBadRequest())
        .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON));

    

【问题讨论】:

请包含完整的堆栈跟踪;指出错误发生在哪一行。除此之外:不清楚你在这里做什么。你看,单元测试的重点是调用某个“被测类”中的方法;然后断言输出;或验证在该操作期间使用的模拟对象。您的代码创建了一个 Mock 对象;并指定对此的调用。我没有看到你在哪里调用你的生产代码?! (也许是因为我不熟悉这些框架,但似乎仍然缺少一些东西)。除此之外:这里不需要 PowerMock。 只有在你想模拟 static 方法或调用 new 时才需要 PowerMock。如果您没有这种情况(并且您应该避免遇到这种情况!)...那么您不需要使用 PowerMock。您可以改用简单的 Mockito。 @GhostCat 我创建了虚拟代码,因为我无法发布实际的项目代码。正如你指出的那样,没有逻辑是正确的。在这里,我创建了项目代码的副本,我必须使用 powermock 编写单元测试。 【参考方案1】:

正如其他人所说,您根本不需要 mockMVC。如果你想测试 REST 端点,你需要的是 TestRestTemplate。 Runwith SpringRunner.class 和 WebEnvironment 设置一样重要。

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class RestServiceApplicationTests 

    private String baseUrl = "http://localhost:8090";

    private String endpointToThrowException = "/employee/2010";

    @Autowired
    private TestRestTemplate testRestTemplate;

    @Test(expected = YearViolationException.class)
    public void testhandleBanNotNumericException() 
        testRestTemplate.getForObject(baseUrl + endpointToThrowException, String.class);

【讨论】:

这段代码似乎告诉 Spring 使用一个随机端口:webEnvironment=WebEnvironment.RANDOM_PORT,但对带有端口 8090 的硬编码 url 运行 @Test。这如何工作?我认为我们应该使用 RANDOM_PORT:AT LocalServerPort private int port; 这是单元测试还是集成测试(@SpringBootTest)?在我看来,这不是单元测试 你是对的,因为我们也在配置环境和上下文,这更像是一个集成测试。这个问题本身存在设计缺陷,因为控制器测试表明集成测试而不是单元测试。【参考方案2】:

从您的设置看起来,您根本不需要使用 Mocks。好像您想加载完整的应用程序上下文并使用 mockMVC 向您的休息控制器发送请求。这实际上是和集成测试!

现在,不幸的是,我们在这里使用 Spring Boot 1.3,所以我不确定 @RunWith(PowerMockRunner.class)@SpringBootTest 的组合是否真的加载了应用程序上下文。检查你的日志,看看是否有,如果没有,试试这个:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration

另一方面,如果您想进行简单的单元测试,则无需加载应用程序上下文。相反,您可以将常规 Mockito 与 @Mock@InjectMocks@RunWith(MockitoJUnitRunner.class) 结合使用,并像调用任何其他被测方法一样直接调用您要测试的方法。

希望对您有所帮助。

【讨论】:

嗨,我想为异常处理程序编写单元测试,即当从其余控制器抛出异常时,异常处理程序会正确处理它。 啊,我不仔细阅读真是太糟糕了。您能否确认您的应用程序上下文已成功加载? 如果我使用 SpringBootTest 、 RunWith(SpringRunner.class) 和 WebAppConfiguration 然后加载 WebApplicationContext 但是当我使用 RunWith(PowerMockRunner.class) 、 WebAppConfiguration 和 SpringBootTest 我得到 java.lang.IllegalArgumentException: WebApplicationContext在我进行自动装配时是必需的 由于你只能使用一个 RunWith,你必须使用 SpringRunner。你需要 PowerMockRunner 做什么?从 Mockito 我知道你可以实现与 MockitoJUnitRunner 在 @Before (MockitoAnnotations.initMocks(this).

以上是关于如何使用 power mock 对 Spring Boot Rest 控制器和异常处理程序进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

使用返回整数列表的 power mock 测试私有方法

高手如何给 Spring MVC 做单元测试?

如何使用Powermock对静态方法进行mock

Spring学习12-Spring利用mock进行单元测试

如何用mockito+spring进行单元测试

spring单元测试Mock,MockBean踩坑及没有真实执行的理解