我有一个需求,我需要开发Hive自定义UDF,它使用Java反射API来调用外部类。
由于我是Java反射的新手,我花了一些时间学习它,并且能够做一个基本的实现。
但是我在为这个实现编写单元测试用例时遇到了一些问题,因为我在模拟反射API时遇到了一些挑战。
下面是配置单元自定义自定义项的示例。
ReverseString.java
public class ReverseString extends GenericUDF {
private StringObjectInspector input;
Class<?> c = null;
Object ob = null;
@Override
public ObjectInspector initialize(ObjectInspector[] arg0) throws UDFArgumentException {
// create an ObjectInspector for the input
ObjectInspector input = arg0[0];
// check to make sure the input is a string
if (!(input instanceof StringObjectInspector)) {
throw new UDFArgumentException("input must be a string");
}
this.input = (StringObjectInspector) input;
System.out.println("Success. Input formatted correctly");
return init(arg0);
}
public ObjectInspector init(ObjectInspector[] arg0) throws UDFArgumentException {
try {
c = Class.forName("com.hive.inherit.DummyUdf");
ob = c.getConstructor().newInstance();
Method method = c.getMethod("print",String.class);
String res = (String) method.invoke(ob,"TEst");
System.out.println("RES: "+res);
} catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
return PrimitiveObjectInspectorFactory.javaStringObjectInspector;
}
@Override
public Object evaluate(DeferredObject[] arg0) throws HiveException {
if (input == null || arg0.length != 1 || arg0[0].get() == null) {
return null;
}
String forwards = input.getPrimitiveJavaObject(arg0[0].get());
System.out.println("forwards:"+forwards);
return reverse(forwards);
}
public Object reverse(String in) {
Object res = null ;
try {
if (this.c != null && this.ob != null) {
Method method = this.c.getMethod("reverse", String.class);
res = (String) method.invoke(this.ob, in);
}else{
c = Class.forName("com.hive.inherit.DummyUdf");
ob = c.getConstructor().newInstance();
Method method = c.getMethod("reverse", String.class);
res = method.invoke(ob, in);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | InstantiationException e) {
e.printStackTrace();
}
return res;
}
@Override
public String getDisplayString(String[] strings) {
return null;
}
}
下面是反射调用的类。
DummyUdf.java
package com.hive.inherit;
public class DummyUdf {
public DummyUdf(){
System.out.println("DummyUdf");
}
public String print(String str){
System.out.println("DummyUdf-str:"+str);
return str;
}
public String reverse(String in) {
int l = in.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < l; i++) {
sb.append(in.charAt(l - i - 1));
}
return sb.toString();
}
}
我试图实现的单元测试用例,
ReverseStringTest.class
@RunWith(MockitoJUnitRunner.class)
public class ReverseStringTest {
@Test
public void testSimpleString() throws HiveException {
//ReverseString r = Mockito.spy(new ReverseString());
ReverseString r = mock(ReverseString.class);
ObjectInspector input = PrimitiveObjectInspectorFactory.javaStringObjectInspector;
when(r.init(Mockito.any())).thenReturn(input);
JavaStringObjectInspector resultInspector = (JavaStringObjectInspector) r.initialize(
new ObjectInspector[] { input });
Text forwards = new Text("hello");
when(r.reverse(Mockito.any())).thenReturn("olleh");
Object result = r.evaluate(new GenericUDF.DeferredObject[] { new GenericUDF.DeferredJavaObject(forwards) });
System.out.println(result);
assertEquals("olleh", resultInspector.getPrimitiveJavaObject(result));
}
}
测试用例失败,出现NullPointerException异常。java.lang.NullPointerException at com.hive.udf.ReverseStringTest.testSimpleString(ReverseStringTest.java:34) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
有人能建议如何适当地嘲笑这个吗?
先谢了。
1条答案
按热度按时间gfttwv5a1#
经过研究,我找到了解决上述问题的有效方法。
下面是工作解决方案:
代替“mock”,我们可以使用“spy”,其中部分对象将被模仿,部分将使用真实的方法调用(如果你想考虑这一点,这也有助于提高你的代码覆盖率)。
你可以参考下面的链接来了解更多关于Mockito 'mock'和'spy'。
Mockito - @Spy vs @Mock
由于我们已经扩展了
GenericUDF
,并且Hive实现首先调用initialize
方法,因此在调用evaluate
之前正确模拟传递的参数是很重要的。