在Mockito中模拟链式调用会导致空指针异常或返回值类型错误。

huangapple go评论81阅读模式
英文:

Mock chain call in Mockito causes NullPointerException or WrongTypeOfReturnValue

问题

我正在尝试模拟这个调用:

requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()

所以我尝试了以下代码:

OperationContext operationContext = new OperationContext(null);
RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class);
when(requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()).thenReturn(operationContext); // 导致 java.lang.NullPointerException

但是这给了我一个 java.lang.NullPointerException

所以我将其修改为:

OperationContext operationContext = new OperationContext(null);
RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class);
RequestTelemetry requestTelemetry = new RequestTelemetry();
when(requestTelemetryContext.getHttpRequestTelemetry()).thenReturn(requestTelemetry);
when(requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()).thenReturn(operationContext);

这给了我:

org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
OperationContext 无法由 getHttpRequestTelemetry() 返回
getHttpRequestTelemetry() 应返回 RequestTelemetry
*** 如果您不确定为什么会出现上述错误,请继续阅读。由于上述语法的特性,可能会发生以下问题:
1. 这个异常 *可能* 发生在编写错误的多线程测试中。请参考 Mockito 关于并发测试限制的 FAQ。
2. 使用 when(spy.foo()).then() 语法来存根化 spy。使用 doReturn|Throw() 系列方法来对 spy 进行存根化更安全 -
    - 有关 Mockito.spy() 方法的更多信息,请参阅javadoc。

编辑:
我尝试了建议的方法:

OperationContext operationContext = new OperationContext(null);

RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class);
RequestTelemetry requestTelemetry = mock(RequestTelemetry.class);
TelemetryContext telemetryContext = mock(TelemetryContext.class);

when(requestTelemetryContext.getHttpRequestTelemetry()).thenReturn(requestTelemetry);
when(requestTelemetry.getContext()).thenReturn(telemetryContext);
when(telemetryContext.getOperation()).thenReturn(operationContext);
ThreadContext.setRequestTelemetryContext(requestTelemetryContext);

不幸的是,这一行 RequestTelemetry requestTelemetry = mock(RequestTelemetry.class); 导致:

Mockito 无法模拟/监视,因为:
- final 类

所以我需要找到一种模拟 final 类的方法。

英文:

I'm trying to mock this call:

requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()

So I tried that:

OperationContext operationContext = new OperationContext(null);
RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class);
when(requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()).thenReturn(operationContext); //causes java.lang.NullPointerException

but that gives me
java.lang.NullPointerException.

So modified it into:

    OperationContext operationContext = new OperationContext(null);
    RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class);
    RequestTelemetry requestTelemetry = new RequestTelemetry();
    when(requestTelemetryContext.getHttpRequestTelemetry()).thenReturn(requestTelemetry);
    when(requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()).thenReturn(operationContext);

that gives me:

> org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
> OperationContext cannot be returned by getHttpRequestTelemetry()
> getHttpRequestTelemetry() should return RequestTelemetry
> *** If you're unsure why you're getting above error read on. Due to the nature of the syntax above problem might occur because:
> 1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
> testing.
> 2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
> - with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.

I do not understand why it can't just mock the whole chain call and I am not trying to return OperationContext by getHttpRequestTelemetry like it states in the error.

EDIT:
I've tried suggested approach:

OperationContext operationContext = new OperationContext(null);

RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class);
RequestTelemetry requestTelemetry = mock(RequestTelemetry.class);
TelemetryContext telemetryContext = mock(TelemetryContext.class);

when(requestTelemetryContext.getHttpRequestTelemetry()).thenReturn(requestTelemetry);
when(requestTelemetry.getContext()).thenReturn(telemetryContext);
when(telemetryContext.getOperation()).thenReturn(operationContext);
ThreadContext.setRequestTelemetryContext(requestTelemetryContext);

unfortunately that line RequestTelemetry requestTelemetry = mock(RequestTelemetry.class); causes:

> Mockito cannot mock/spy because :
> - final class

so i need to find a way to mock final class.

答案1

得分: 2

你需要在调用链的每个阶段返回模拟对象,并且每个模拟对象都应该返回下一个模拟对象,例如:

a().b().c().d()

因此,a() 应该返回一个模拟对象,它被配置为返回模拟对象 b,依此类推:

when(a()).thenReturn(mockA)
when(mockA.b()).thenReturn(mockB)

等等。

英文:

You need to return the mock at each stage of your call chain, and each mock should return the next mock object e.g.

a().b().c().d()

so a() should return a mock, which is configured to return a mock b etc.

when(a()).thenReturn(mockA);
when(mockA.b()).thenReturn(mockB);

etc.

答案2

得分: 1

你有两个问题。第一个问题涉及到链式操作的模拟 (requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation())。为了解决这个问题而不需要模拟中间的每个类,你可以在模拟时使用 RETURNS_DEEP_STUBS

OperationContext operationContext = new OperationContext(null);
RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class, RETURNS_DEEP_STUBS);
when(requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()).thenReturn(operationContext);

第二个问题是Mockito不能模拟final类 - 恐怕这是无法解决的,你无法模拟一个final类。

英文:

You have two issues here. the first one is with the mock of chained operations (requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()). to solve this without mocking every class in the middle, you can use RETURNS_DEEP_STUBS when mocking:

OperationContext operationContext = new OperationContext(null);
RequestTelemetryContext requestTelemetryContext = mock(RequestTelemetryContext.class, RETURNS_DEEP_STUBS);
when(requestTelemetryContext.getHttpRequestTelemetry().getContext().getOperation()).thenReturn(operationContext);

The second issue is that mockito cannot mock final classes - this is I'm afraid not solvable, you can't mock a final class

huangapple
  • 本文由 发表于 2020年8月28日 17:10:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63630806.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定