You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何Mock Java反射实现Hive自定义UDF的单元测试?

解决Hive自定义UDF反射调用的单元测试Mock空指针问题

我需要开发一个通过Java反射调用外部类的Hive自定义UDF,基础实现已完成,但编写单元测试时Mock反射API遇到NullPointerException,恳请指导正确的Mock方式解决问题。

相关代码示例

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.java

@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));
    }
}

报错信息

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)

问题分析与解决方案

空指针原因

你当前使用mock(ReverseString.class)创建的是完全模拟对象,Mockito会给所有方法默认返回null。虽然你Mock了init方法,但initialize方法的真实逻辑完全没执行——其中给this.input赋值的步骤被跳过,导致后续调用evaluate时,input为null,触发空指针异常。

正确Mock方式

改用Spy对象(基于真实对象的部分模拟),保留initialize等核心方法的真实逻辑,仅Mock反射相关的init和reverse方法,避免真实调用外部类DummyUdf:

修改后的测试代码:

@RunWith(MockitoJUnitRunner.class)
public class ReverseStringTest {

    @Test
    public void testSimpleString() throws HiveException {
        // 创建Spy对象,保留真实对象的核心逻辑
        ReverseString r = Mockito.spy(new ReverseString());
        ObjectInspector inputInspector = PrimitiveObjectInspectorFactory.javaStringObjectInspector;

        // 使用doReturn跳过真实init方法的反射逻辑,直接返回指定结果
        doReturn(inputInspector).when(r).init(Mockito.any());

        // 执行真实的initialize方法,确保input成员变量被正确赋值
        JavaStringObjectInspector resultInspector = (JavaStringObjectInspector) r.initialize(
                new ObjectInspector[] { inputInspector });

        Text forwards = new Text("hello");
        // Mock reverse方法,直接返回预期的反转结果
        doReturn("olleh").when(r).reverse(Mockito.anyString());

        // 执行evaluate方法
        Object result = r.evaluate(new GenericUDF.DeferredObject[] { new GenericUDF.DeferredJavaObject(forwards) });

        // 断言验证结果
        assertEquals("olleh", resultInspector.getPrimitiveJavaObject(result));
    }
}

关键说明

  1. Spy vs Mock:Spy会执行真实对象的方法逻辑,只有被Mock的方法才会替换行为;而Mock是完全模拟,所有方法默认返回null,这是解决空指针的核心。
  2. doReturn的使用:用doReturn(...).when(r).init(...)而不是when(r.init(...)).thenReturn(...),因为后者会先执行一次真实的init方法,可能触发不必要的反射调用,前者直接指定返回值,跳过真实方法执行。
  3. 代码优化建议:如果后续需要更灵活的测试,可以把反射获取DummyUdf的逻辑抽成独立方法(比如private DummyUdf getDummyUdfInstance()),这样只需Mock这个方法返回一个Mock的DummyUdf对象,测试会更简洁,也符合单一职责原则。

内容的提问来源于stack exchange,提问作者nambastha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:33:10