Mockito 模拟调用依赖私有方法的方法时抛出空指针异常
空指针异常产生的根本原因
当前遇到的NPE,本质是Mockito对Scala单例的mock机制和单例本身的初始化逻辑冲突导致的:
- 当你使用mockito-scala提供的
withObjectMocked方法包裹测试逻辑时,Mockito会直接在字节码层面把myObject的全局引用替换成它生成的mock代理对象,完全不会执行单例原本的类初始化代码 - 对应业务代码,单例正常初始化时本该执行
private val mylog = new Logger("Title")给日志对象赋值,但mock代理对象根本不会跑这段初始化逻辑,所有原本定义在单例里的实例字段(包括mylog)都会保持Java默认的null值 - 测试代码里给
myMethod配置了thenCallRealMethod(),意味着mock会走真实的方法逻辑,代码执行到mylog.info("...")这行时,操作值为null的mylog字段,就直接抛出了NullPointerException。
可行的解决方向
- 优先避免无意义的整体单例mock。如果测试目标只是验证
myMethod的返回值逻辑,根本不需要用withObjectMocked包裹整个测试块:直接调用原生的myObject.myMethod(入参)即可,此时单例的初始化逻辑会正常执行,mylog字段会被正常赋值,从根源上避免NPE。只有当你需要让myMethod返回预设的模拟值、屏蔽真实逻辑时,再使用单例mock的语法,这种场景下根本不会触发方法内部的日志调用,自然不会出现空指针。 - 如果业务要求必须在单例mock的作用域内调用真实的
myMethod逻辑,可以手动给mock代理的空字段做赋值:- 借助
Whitebox.setInternalState这类反射工具,在配置thenCallRealMethod()之前,手动把mock代理对象里的mylog字段赋值为你准备好的测试用Logger实例,避免字段为null。
反射操作时要注意:Scala单例的字段实际挂载在单例类的静态实例上,定位字段时要对应字节码层面的类结构,不要错定位到类本身的静态字段上。
- 借助
- 从代码结构上优化单例的依赖关系。把硬编码在单例内部的
mylog实例调整为可外部注入的依赖,不在单例初始化逻辑里硬编码创建Logger对象,从根源上消除单例初始化和mock代理初始化的逻辑冲突。
内容的提问来源于stack exchange,提问作者FTK
相关产品推荐
相关产品推荐

