【问题标题】:How to write JUnit Tests using Mockito on JSON transformation?如何在 JSON 转换上使用 Mockito 编写 JUnit 测试?
【发布时间】:2021-05-04 11:13:59
【问题描述】:

我有一个服务类,它使用 DynamoDB 将 JSON 从一种模式转换为另一种模式。 这个类有各种方法来操作 JSON 字段,如下所示。我必须写 JUnit 使用 Mockito 对此代码进行测试。 convertToProviderJson 方法将来自第 3 方的 JSON 转换为预定义模板,以下方法正在处理转换后的 JSON 上的细节。

我是 JUnit 和 Mockito 的新手,我应该如何继续?

```
@Service
public class ServiceClass {
    
    public String convertToProviderJson(String consumerString, String providerTemplateJson)
            throws JsonProcessingException {
        //create ObjectMapper instance
        ObjectMapper objectMapper = new ObjectMapper();

        //convert json file to map
        String currentFieldName = "";
        String currentTemplateKey = "";
        boolean templateMatchError = false;
        Map<?, ?> providerMap;
        Map<String, Object> providerOutputMap = new LinkedHashMap<>();
        System.out.println("Provider JSON");
        if(!UtilityClass.isJSONValid(consumerString)) {
            throw new MalformedJsonException("Incorrect Consumer Input JSON.");
        }

        if(!UtilityClass.isJSONValid(providerTemplateJson)) {
            throw new MalformedJsonException("Incorrect Provider Template JSON.");
        }
        try {
            JSONObject consumerJson = new JSONObject(consumerString);
            providerMap = objectMapper.readValue(providerTemplateJson, Map.class);

            //iterate over Provider Template map.
            for (Map.Entry<?, ?> entry : providerMap.entrySet()) {
                String key = (String) entry.getKey();
                currentTemplateKey = key;
                String value = (String) entry.getValue();
                Pattern p = Pattern.compile(TransformationConstants.TEMPLATE_FUNCTION_REGEX);

                Matcher matcher = p.matcher((CharSequence) entry.getValue());
                if (matcher.matches()) {
                    String[] splitString = value.split(LEFT_ROUND_BRACKET);
                    String functionName = splitString[0];
                    String fieldName = splitString[1].split(RIGHT_ROUND_BRACKET)[0];
                    currentFieldName = fieldName;
                    Object fieldValue = invokeFunction(consumerJson, functionName, fieldName);
                    providerOutputMap.put(key, fieldValue);
                } else {
                    templateMatchError = true;
                    break;
                }
            }

        } catch(JsonEOFException e) {
            throw new MalformedJsonException("Incorrect Provider Template JSON.");
        } catch (Exception e) {
            throw new MalformedJsonException("Field '" + currentFieldName + "' missing in input json.");
        }
        if(templateMatchError) {
            throw new MalformedJsonException("Value for Field '" + currentTemplateKey
                    + "' in template JSON is not in correct format.");
        }
        String outputJson = objectMapper.writeValueAsString(providerOutputMap);
        System.out.println("Provider JSON: " + outputJson);
        return outputJson;
    }

    private Object invokeFunction(JSONObject consumerJson, String functionName, String fieldName)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        TransformationService obj = new TransformationService();
        Method method;
        method = obj.getClass().getMethod(functionName, JSONObject.class, String.class);
        return method.invoke(obj, consumerJson, fieldName);
    }

    public Object getField(JSONObject jsonObject, String fieldName) throws JSONException {
        if(jsonObject.has(fieldName)) {
            return jsonObject.get(fieldName);
        }
        throw new MalformedJsonException("Field '" + fieldName + "' missing in input json.");
    }
}

在阅读了一些文章后,我尝试在 getField() 方法上编写单元测试。这是我的代码,我知道它错了,我该如何处理?

@Test   
public void hasFieldTest() {
    JSONObject obj = new JSONObject();
    obj.put("id", "1");
    obj.put("name", "divyanka");
    when(((Object) transformationMock.getField(jsonObjmock, "name")).thenReturn(objectMock);
    JSONAssert.assertEquals("{id:1}", obj, 'strict':false);     
}

【问题讨论】:

  • ..当你测试这个服务(类)时,你不应该模拟它。
  • 忘记了:嗨,欢迎来到Stack Overflow! :-)

标签: java json unit-testing mockito junit5


【解决方案1】:

要测试(getField 的方法)ServiceClass,我会接近:

import static org.junit.jupiter.api.Assertions.assertThrows;
// ...

class ServiceClassTest {

   //Object/Class under test:
   private ServiceClass testee = new ServiceClass(); // create new or "obtain somewhere" (like @Autowired in Spring testing...)

  //... test all methods, lines:

  @Test
  public void testGetFieldOK() {
      // prepare "good" object:
      JSONObject obj = new JSONObject();
      obj.put("foo", "bar");

      // TEST/invoke (!):
      Object result = testee.getField(obj, "foo");
     
     // assert (with your assertion framework/style):
     // result is not null AND result == "bar"  
     // done!
  }

  @Test
  public void testGetFieldException() {
      // prepare "bad" object:
      JSONObject obj = new JSONObject();

      // Test/Expect exception -> https://stackoverflow.com/a/40268447/592355 ->:
      MalformedJsonException thrown = assertThrows(
       MalformedJsonException.class,
       () -> testee.getField(obj, "foo"),
       "Expected getField(obj, \"foo\") to throw, but it didn't"
      );

      //assert on exception (message):
      assertTrue(thrown.getMessage().contains("Field 'foo' missing in input json.")); 

  }

  //create more tests like that... (-> coverage),
  //.. WHEN real parameters, associated objects and class (invocations) are not applicable, mock them!
}

感谢:https://stackoverflow.com/a/40268447/592355

并总结主要主题:

  • 测试(尽可能)真实(尽可能)的对象。
  • (尝试)实现覆盖。
  • 更喜欢“真正的实现”而不是模拟,并且只有在真正的实现不适用/成本太高时才使用它们。 (接口,外部代码/系统,...内部代码/系统,“接线”成本太高/不适用,并且被其他测试覆盖。)

所以在您的代码中:ObjectMapperTransformationService 看起来像是可能的模拟候选对象,但这并不值得,因为它们是在本地创建的(在测试方法中)。

UtilityClass 也可能被(权力)嘲笑,但值得吗!?? :-)

如果UtilityClass 是(外部)生产性(收费)API,您可能想要模拟它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    • 2019-06-21
    • 2020-02-27
    相关资源
    最近更新 更多