使用自定义异常处理测试REST端点

时间:2016-11-16 08:00:00

标签: java testing spring-boot exception-handling mockmvc

我正在使用Spring微服务(模块)开发项目,我想使用2016-11-16 15:56:35.418 Projc[43766:2236752] +[UIColor bt_colorWithBytesR:G:B:]: unrecognized selector sent to class 0x10fd17948 2016-11-16 15:56:35.504 Projc[43766:2236752] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[UIColor bt_colorWithBytesR:G:B:]: unrecognized selector sent to class 0x10fd17948' *** First throw call stack: ( 0 CoreFoundation 0x00000001116aaf45 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x0000000110998deb objc_exception_throw + 48 2 CoreFoundation 0x00000001116b346d +[NSObject(NSObject) doesNotRecognizeSelector:] + 205 3 CoreFoundation 0x0000000111600eea ___forwarding___ + 970 4 CoreFoundation 0x0000000111600a98 _CF_forwarding_prep_0 + 120 5 Projc 0x000000010ccb95b0 -[BTUI borderColor] + 64 6 Projc 0x000000010cc9abf4 -[BTDropInContentView initWithFrame:] + 964 7 UIKit 0x000000010f0abe3d -[UIView init] + 62 8 Projc 0x000000010ccc329e -[BTUIThemedView init] + 62 9 Projc 0x000000010cca5fed -[BTDropInViewController initWithAPIClient:] + 285 10 Projc 0x000000010cb0998b __51-[RegisterController basicPlanClicked]_block_invoke + 411 11 Projc 0x000000010cac1a66 __59-[downloadJson:endPointString:WithHandler:]_block_invoke + 214 12 CFNetwork 0x000000010d4f5b3d __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19 13 CFNetwork 0x000000010d507fb6 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 302 14 Foundation 0x000000011060c3b8 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7 15 Foundation 0x00000001105480e5 -[NSBlockOperation main] + 101 16 Foundation 0x000000011052b036 -[__NSOperationInternal _start:] + 646 17 Foundation 0x000000011052ac47 __NSOQSchedule_f + 194 18 libdispatch.dylib 0x0000000111e0649b _dispatch_client_callout + 8 19 libdispatch.dylib 0x0000000111dee2af _dispatch_main_queue_callback_4CF + 1738 20 CoreFoundation 0x000000011160b2e9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9 21 CoreFoundation 0x00000001115cc8a9 __CFRunLoopRun + 2073 22 CoreFoundation 0x00000001115cbe08 CFRunLoopRunSpecific + 488 23 GraphicsServices 0x0000000112291ad2 GSEventRunModal + 161 24 UIKit 0x000000010f01f30d UIApplicationMain + 171 25 Projc 0x000000010cba86bf main + 111 26 libdyld.dylib 0x0000000111e3b92d start + 1 27 ??? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException (lldb) 测试我的REST端点。我的测试适用于请求有效但在请求无效的URL时无效的情况。通过不工作我的意思是我的自定义异常处理程序(MockMvc)没有被调用,抛出异常并且测试失败。

我的异常处理程序和测试类在不同的模块中实现。

common-module(ExceptionHandler)

@ControllerAdvice

国模块

这是我的REST端点和Testing类的实现。公共模块依赖项包含在此模块的pom.xml中,并且通过主类扫描包。

CountryApplication.java

@ControllerAdvice
public class CoreExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<ErrorMessageDTO> handleException(Exception ex, HttpServletRequest request) {

        // Getting servlet request URL
        String uri = request.getRequestURI();
        HttpStatus a;

        ErrorMessageDTO errorMessage;

        if (ex instanceof CoreException) {
            CoreException e = (CoreException) ex;
            ...
            errorMessage = new ErrorMessageDTO(e, uri);
        } else {
            errorMessage = new ErrorMessageDTO(ex, uri);
            ...
        }
        return new ResponseEntity<ErrorMessageDTO>(errorMessage, a);
    }
}

CountryService.java

这是我的Service类中的一个方法。

@EnableCaching
@EnableDiscoveryClient
@EnableAspectJAutoProxy
@SpringBootApplication(scanBasePackages = {
    "com.something1.something2.something3.common.exception",
    "com.something1.something2.something3.common.util.logged",
    "com.something1.something2.something3.country"
})
public class CountryApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(CountryApplication.class, args);
    }
    ...
}

CountryServiceTest.java

@GetMapping("/{id:\\d+}")
public CountryDTO getCountryById(@PathVariable("id") Integer id) throws CoreException {

    Country countryEntity = this.countryRepository.findOne(id);

    // requesting for id that does not exist
    if (countryEntity == null) {
        throw new CoreException(CoreError.ENTITY_NOT_FOUND);
    }
    return this.countryMapper.daoToDto(countryEntity);
}

正如我上面所描述的,问题是在测试方法的第二个请求中,@SpringBootTest @AutoConfigureMockMvc @AutoConfigureTestDatabase @RunWith(SpringRunner.class) public class CountryServiceTest { ... @Autowired private MockMvc mockMvc; @Test public void getByIdTest() throws Exception { // Get by id exists mockMvc.perform(get("/2")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andDo(print()); // Get by id not exists. NOT WORKING mockMvc.perform(get("/100000")) .andExpect(status().isNotFound()) .andExpect(content().contentType(contentType)); } } 没有被调用,测试失败了抛出:

CoreExceptionHandler

公共模块的依赖性配置得很好(至少当我在非测试模式下部署时),因为我也将它用于其他事情,而且当我没有测试时会调用ExceptionHandler。

另一个奇怪的事情是,当我部署我的测试时,Spring Boot的日志显示NestedServletException: Request processing failed; nested exception is com.something1.something2.something3.common.exception.CoreException被检测到。这就是这条线。 CoreExceptionHandler

1 个答案:

答案 0 :(得分:0)

有两个问题如下所述:

(1)您的MockMvc课程中没有为CountryServiceTest对象设置ControllerAdvice,如下所示:

MockMvc mockMvc = standaloneSetup(yourController)
        .setHandlerExceptionResolvers(new CoreExceptionHandler())
        .build();

(2)由于CoreException是Spring容器NestedServletException的包装器,因此您需要使用exception.getCause()检查您的异常,如下所示:< / p>

@ControllerAdvice
    public class CoreExceptionHandler {

        @ExceptionHandler(value = Exception.class)
        public ResponseEntity<ErrorMessageDTO> handleException(Exception ex, 
            HttpServletRequest request) {

            // Getting servlet request URL
            String uri = request.getRequestURI();
            HttpStatus a;

            ErrorMessageDTO errorMessage;

            //check with exception cause
            if (ex.getCause() instanceof CoreException) {
                CoreException e = (CoreException) ex;
                ...
                errorMessage = new ErrorMessageDTO(e, uri);
            } else if (ex instanceof CoreException) {
               //this block will be used only when direct CoreException triggered
                CoreException e = (CoreException) ex;
                ...
                errorMessage = new ErrorMessageDTO(e, uri);
            } else {
                errorMessage = new ErrorMessageDTO(ex, uri);
                ...
            }
            return new ResponseEntity<ErrorMessageDTO>(errorMessage, a);
        }
    }

另外,我建议不要在一个通用方法中处理所有异常类型,这将非常难以支持/维护,而是使用多个CoreExceptionHandler方法/类拆分@ExceptionHandler