模拟方法的NullPointerException

时间:2015-11-18 13:04:30

标签: java junit mockito powermockito

对于以下方法:

public void parseJSONData(String jsonData) throws JSONException {
        if (jsonData == null || jsonData.length() == 0)
            throw new JSONException("Empty JSON string");
        JSONObject obj = new JSONObject(jsonData);
        JSONArray arr = obj.getJSONArray("beacons");        

        for (int i = 0; i < arr.length(); i++) {
            JSONObject item = arr.getJSONObject(i);
            addBeaconData(item); 
        }
    }

我写了以下测试方法

    @Test
        public void testParseJSONData() throws Exception {

            String jsonData="testJson";    
            JSONObject jsonObject = PowerMockito.mock(JSONObject.class);  
            PowerMockito.whenNew(JSONObject.class).withArguments(jsonData).thenReturn(jsonObject);

            JSONArray arr = PowerMockito.mock(JSONArray.class);
            PowerMockito.when(jsonObject.getJSONArray(Mockito.anyString())).thenReturn(arr);        

            JSONObject item = new JSONObject();    
            PowerMockito.when(arr.length()).thenReturn(1);    
            PowerMockito.when(arr.getJSONObject(0)).thenReturn(item);

            BeaconDataParser jsonParser=PowerMockito.spy(new BeaconDataParser());
            PowerMockito.doNothing().when(jsonParser).addBeaconData(item); 
            jsonParser.parseJSONData(jsonData);
    }

结果是带有for循环的行的空指针异常。可能是失败的原因。 arr对象的length方法被模拟。 有2个类注释:

@RunWith(PowerMockRunner.class)
@PrepareForTest({JSONArray.class,JSONObject.class})

4 个答案:

答案 0 :(得分:1)

在你的方法中是:

  JSONObject obj = new JSONObject(jsonData);
        JSONArray arr = obj.getJSONArray(obj);  

但在你的测试中是:

PowerMockito.when(jsonObject.getJSONArray(Mockito.anyString())).thenReturn(arr);

您需要调用getJSONArray(使用任何String),但实际上是使用JSONObject调用它

这是NPE的问题,但obj.getJSONArray(obj)应该做什么?它是否正确?看起来你应该为数据使用一些键,而不是你调用它的对象。

可能错误符合:

 JSONArray arr = obj.getJSONArray(obj); 

您期望在这里找到什么关键?它应该是:

JSONArray arr = obj.getJSONArray("key for your array"); 

答案 1 :(得分:0)

在评论中借调Jaroslaw,绝对没有理由让JSON解析中涉及一个模拟框架。 JSON是经过充分指定和经过充分测试的,并且不会成为错误的来源。

相反,将各种beacon JSON字符串传递给您的方法,每次测试一次,并在必要时监视对addBeaconData的调用。但是,对所有测试的最佳测试只是测试对parseJSONData的调用将导致您期望的未模拟的BeaconDataParser 状态

答案 2 :(得分:0)

也许第二次调用arr.length(),尝试:

public void parseJSONData(String jsonData) throws JSONException {
        if (jsonData == null || jsonData.length() == 0)
            throw new JSONException("Empty JSON string");
        JSONObject obj = new JSONObject(jsonData);
        JSONArray arr = obj.getJSONArray("beacons");        
        int size = arr.length();

        for (int i = 0; i < size; i++) {
            JSONObject item = arr.getJSONObject(i);
            addBeaconData(item); 
        }
    }

答案 3 :(得分:0)

当您模拟方法本地实例化时,您必须在调用该构造函数的 prepareForTest 中添加类名。 这里 BeaconDataParser 是使用new运算符创建 JSONObject 的类。

以下是其他观察结果:

  

1)不需要在prepareForTest中添加JSONArray.class,JSONObject.class,因为它们只是普通的java对象。

  2)JSONObject有多个重载的构造函数,所以你需要在使用whenNew进行模拟时指定参数类型。

您的测试类应该是这样的。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ BeaconDataParser.class })
public class BeaconDataParserTest {

 @Test
 public void testParseJSONData() throws Exception {

    String jsonData = "testJson";
    JSONObject jsonObject = PowerMockito.mock(JSONObject.class);
    PowerMockito.whenNew(JSONObject.class).withParameterTypes(String.class). withArguments(jsonData)
            .thenReturn(jsonObject);

    JSONArray arr = PowerMockito.mock(JSONArray.class);
    PowerMockito.when(jsonObject.getJSONArray(Mockito.anyString()))
            .thenReturn(arr);

    JSONObject item = new JSONObject();
    PowerMockito.when(arr.length()).thenReturn(1);
    PowerMockito.when(arr.getJSONObject(0)).thenReturn(item);

    BeaconDataParser jsonParser = PowerMockito.spy(new BeaconDataParser());
    PowerMockito.doNothing().when(jsonParser).addBeaconData(item);
    jsonParser.parseJSONData(jsonData);
 }

}