尝试使用 powermockito 模拟包私有方法时出现空指针异常

Getting nullpointer exception when trying to mock package private method using powermockito

我正在使用 mockito 和 powermockito 为以下控制器 class 编写测试用例。

@Controller
@RequestMapping("/course")
public class LoginController implements MessageConstants {
     protected static Logger logger = org.apache.log4j.Logger.getLogger("LoginController");

     @Autowired
     public LoginServiceDao LoginServiceDao;

     @RequestMapping(value = "/access", method = RequestMethod.POST)
     public ResponseEntity<String> validateRequest(@RequestBody String request, @RequestHeader(HttpHeaders.AUTHORIZATION) String jwt) {

     try {
            String key = LoginServiceDao.getKey();
            Properties prop = new Properties();
            InputStream input = new FileInputStream("/data/properties/conf.properties");
            prop.load(input);
            String claims = parseJwt(jwt, key);
            String message = "";
            String modifiedJson = "";
            String response = "";
            if (claims.equals("Unsupported JWT") || claims.equals("Malformed JWT") || claims.equals("Signature Mismatch")
                        || claims.equals("Unable to Parse JWT") || claims.equals("Invalid Claim Arguments")
                        || claims.equals("Invalid JWT")) {
                    message = getPropertiesMessage(MessageConstants.ERROR_JACPLUS_JWT_INVALID);
                    response = prepareResponse(response, message);
                    return new ResponseEntity<String>(response, HttpStatus.UNAUTHORIZED);
                }
            if (claims.equals("Expired JWT")) {
                    message = getPropertiesMessage(MessageConstants.ERROR_JACPLUS_JWT_EXPIRED);
                    response = prepareResponse(response, message);

                    return new ResponseEntity<String>(response, HttpStatus.REQUEST_TIMEOUT);
                }
    }

    private String parseJwt(String jwt, String key) {
            logger.debug("Inside parseJwt method");
            Jws<Claims> jwsClaims = null;
            try {
                InputStream input = new FileInputStream("/data/properties/conf.properties");
                Properties prop = new Properties();
                prop.load(input);
                int skewTime = Integer.valueOf(prop.getProperty("skewTime")).intValue();
                jwsClaims = Jwts.parser().setSigningKey(key.getBytes("UTF-8")).setAllowedClockSkewSeconds(skewTime).parseClaimsJws(jwt);

           } catch (Exception exp) {
                if (exp instanceof UnsupportedJwtException) {
                    logger.error("Inside parseJwt method - Unsupported JWT", exp);
                    return "Unsupported JWT";
                } else if (exp instanceof MalformedJwtException) {
                    logger.error("Inside parseJwt method - Malformed JWT", exp);
                    return "Malformed JWT";
                } else if (exp instanceof SignatureException) {
                    logger.error("Inside parseJwt method - Signature Mismatch", exp);
                    return "Signature Mismatch";
                } else if (exp instanceof InvocationTargetException) {
                    logger.error("Inside parseJwt method - Unable to Parse JWT", exp);
                    return "Unable to Parse JWT";
                } else if (exp instanceof ExpiredJwtException) {
                    logger.error("Inside parseJwt method - Expired JWT", exp);
                    return "Expired JWT";
                } else if (exp instanceof IllegalArgumentException) {
                    logger.error("Inside parseJwt method - Invalid Claim Arguments", exp);
                    return "Invalid Claim Arguments";
                } else {
                    logger.error("Inside parseJwt method - Invalid JWT", exp);
                    return "Invalid JWT";
                }
            }
            return jwsClaims.toString();
        }

    }

Test class for LoginController controller class.

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({LoginController.class,FileInputStream.class,Jwts.class})
    public class LoginControllerTest {

        @InjectMocks
        public LoginServiceDaoImpl loginServiceDaoImpl;

        @Mock
        public LoginServiceDao loginServiceDao;

        private MockMvc mockMvc;

        @InjectMocks
        private LoginController loginController;

        @Spy
        private Properties prop = new Properties();

        @Mock
        private FileInputStream stream;

        private LoginController classUnderTest;

        String jsonRequestBody;

        String key = "test";

        String jwt = "testJwt";


        @Before
        public void setUp()  {
            mockMvc = MockMvcBuilders.standaloneSetup(loginController).build();
            prop.setProperty("skewTime", "1000");
            classUnderTest = PowerMockito.spy(new LoginController());
            jsonRequestBody = "{....}";

            MockitoAnnotations.initMocks(this);
        }

            @Test
            public void testValidateCampionRequest() throws Exception {

                PowerMockito.whenNew(FileInputStream.class).withArguments(Mockito.anyString()).thenReturn(stream);
                PowerMockito.whenNew(Properties.class).withNoArguments().thenReturn(prop);

                Jwts jwts = mock(Jwts.class);
                PowerMockito.mockStatic(Jwts.class);
//NullPointer Exception here
                Mockito.when(jwts.parser().setSigningKey(key.getBytes("UTF-8")).setAllowedClockSkewSeconds(anyLong()).parseClaimsJws(anyString())).thenThrow(new ExpiredJwtException(null,null,null));


                //PowerMockito.doNothing().when(classUnderTest, "parseJwt", jwt, key);

                RequestBuilder request = MockMvcRequestBuilders
                        .post("/course/access")
                        .header("Authorization","Bearer " + jwt)
                        .content(jsonRequestBody)
                        .accept(MediaType.APPLICATION_JSON);

                MvcResult result = mockMvc.perform(request)
                        .andExpect(status().isRequestTimeout())
                        .andReturn();


            }

    }

但我得到了上述测试用例的 nullpointerexception。我在发生空异常的测试 class 中添加了注释。

parseJwt方法是封装private方法。

我还尝试如下模拟 parseJwt 方法

PowerMockito.doNothing().when(classUnderTest, "parseJwt", anyString(), anyString());

然后 parseJwt 方法 return IllegalArgumentException 异常。

我想 return 从 parseJwt(jwt, key) 方法中声明等于 "Expired JWT"。

我认为错误与可见性无关public/package/private

            Jwts jwts = mock(Jwts.class);
            Mockito.when(jwts.parser().someMethod().someOtherMethod()...

因为 jws 是一个模拟所有模拟的方法return 类型特定的默认值(null、0 或 false)

所以如果您不添加像

这样的代码,jwts.parser() 将 return 为 null
 Mockito.when(jwts.parser()).thenReturn(new MyParser());

或者您可以使用

 Mockito.when(jwts.parser().thenThrow(new ExpiredJwtException(null,null,null));