java - 如何在 JSON 转换上使用 Mockito 编写 JUnit 测试?
问题描述
我有一个服务类,它使用 DynamoDB 将 JSON 从一种模式转换为另一种模式。这个类有各种方法来操作 JSON 字段,如下所示。我必须使用 Mockito 对此代码编写 JUnit 测试。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);
}
解决方案
为了测试(的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
并总结主要主题:
- 测试(尽可能)真实(尽可能)的对象。
- (试图)实现覆盖。
- 与模拟相比,更喜欢“真正的实现”,并且仅在实际实现不适用/成本太高时才使用它们。(接口,外部代码/系统,......内部代码/系统,“接线”成本太高/不适用,并且被其他测试覆盖。)
所以在你的代码中:ObjectMapper
并且TransformationService
看起来像是可能的模拟候选人,但这不值得,因为它们是在本地创建的(在测试方法中)。
UtilityClass
也可能被(权力)嘲笑,但值得吗!??:-)
如果UtilityClass
是(外部)生产性(收费)API,您可能想要模拟它。
推荐阅读
- amazon-web-services - 调用一次 lambda 函数,执行两次
- alexa - 如何修复 Alexa 技能中的“AskSdk.GenericRequestDispatcher 错误:无法找到合适的请求处理程序”错误
- android - 一个自签名证书来统治他们?Chrome、Android 和 iOS
- swift - SwiftUI 中的 ActionSheet/Sheet 到模态
- multithreading - 是否可以在 Websocket 交付线程上安排任务?
- cookies - JMETER 此站点未在 P3P 标头中指定策略 ERROR
- python - 如何使用 Python Elementtree 修改 XML 元素
- azure - 在 Azure API 管理中,是否可以有一个允许斜杠(例如,路径)的模板参数?
- java - 如何在 Java 中使用 Dropwizard 获取 swagger.json
- ruby - Ruby 2.6.3 - 以符号为键的“警告”哈希会产生 ArgumentError