gpt4 book ai didi

java - Mockito Spy 看不到正确的原始值

转载 作者:行者123 更新时间:2023-12-05 05:01:11 25 4
gpt4 key购买 nike

我面临以下问题,当间接更新 spy 对象的字段时, spy 看不到 primitive 字段的更新,而它看到的是 reference一次。

举个例子:

import org.junit.Test;
import org.mockito.Mockito;

import java.util.function.Consumer;
import java.util.concurrent.atomic.AtomicBoolean;

public class MyTest {

class Actor {
Consumer<Boolean> consumer;

void init(Consumer<Boolean> consumer){
this.consumer = consumer;
}

void act(boolean flag){
this.consumer.apply(flag);
}
}

class TestClass {
boolean field = true;
AtomicBoolean refField = new AtomicBoolean(true);

TestClass(Actor actor){
actor.init( flag -> {
System.out.println("Changing field to " + flag);
field = flag;
refField.set(flag);
});
}

void call(){
this.doSomething(field);
}

void callRef(){
this.doSomething(refField.get());
}

void doSomething(boolean flag){
System.out.println("#doSomething(" + flag + ")");
}
}

@Test
public void test(){
// given an actor and a spied TestClass
Actor actor = new Actor();
TestClass spy = Mockito.spy(new TestClass(actor));

// when invoking the action with the primitive
spy.call();
// then expect correct invocation
Mockito.verify(spy, Mockito.times(1)).doSomething( true );

// when invoking the action with the ref field
spy.callRef();
// then expect correct invocation
Mockito.verify(spy, Mockito.times(2)).doSomething( true );

// when changing the flag to 'false'
actor.act( false );

// and invoking the action with the refField
spy.callRef();
// then expect correct invocation
Mockito.verify(spy, Mockito.times(1)).doSomething(false);

// when invoking the action with the primitive
spy.call();
// then method is NOT invoked as expected !!!!!!!
Mockito.verify(spy, Mockito.times(2)).doSomething(false);
}
}

最后一次验证将失败,因为该方法是用第一个原始值调用的。

我想知道为什么会这样?这是预期的行为吗?运行上述测试,将产生以下日志记录:

#doSomething(true)
#doSomething(true)
Changing flag to false
#doSomething(false)
#doSomething(true)

我希望最后一条日志语句以 false 调用。

对以上内容有什么见解吗?

PS: Version=mockito-core:2.25.0

最佳答案

您的系统中有 2 个 TestClass 对象实例:

  • 原始实例
  • spy 实例

spy 是从原始实例创建的。

  • 原始字段(如 boolean 字段)被复制,每个 TestClass 都包含
  • 引用被复制,但底层引用对象是共享的(refField)

让我们进行以下更改以观察:

TestClass(Actor actor){
actor.init( flag -> {
// print referenced TestClass instance
System.out.println("from actor: " + TestClass.this);
System.out.println("Changing field to " + flag);
field = flag;
refField.set(flag);
});
}

@Test
public void test(){
// given an actor and a spied TestClass
Actor actor = new Actor();
TestClass testClassOriginal = new TestClass(actor);
TestClass spy = Mockito.spy(testClassOriginal);
System.out.println("org" + testClassOriginal);
System.out.println("spy" + spy);

// when changing the flag to 'false'
actor.act( false );
System.out.println("After change org: " + testClassOriginal.field);
System.out.println("After change spy: " + spy.field);
}

给出以下输出:

org: mypackage.MyTest$TestClass@4218500f
spy: mypackage.MyTest$TestClass$MockitoMock$2115616475@5c10f1c3
from actor: mypackage.MyTest$TestClass@4218500f
Changing field to false
After change org: false
After change spy: true

可以清楚的看到:

  • TestClass 的两个实例
  • Actor 看到的是原始对象,而不是 spy
  • 当 actor 对原始字段进行更改时,它会更改原始字段
  • spy 中的原始字段保持不变

可能的解决方案

你需要让 Actor 看到 spy ,而不是原来的对象。为此,必须将 TestClass 的创建与注册消费者分离。

class TestClass {
boolean field = true;
AtomicBoolean refField = new AtomicBoolean(true);

TestClass(){
}

void consumeFlag(Boolean flag) {
System.out.println("from actor: " + this);
System.out.println("Changing field to " + flag);
field = flag;
refField.set(flag);
}
// ...
}

@Test
public void test(){
// given an actor and a spied TestClass
Actor actor = new Actor();
TestClass spy = Mockito.spy(new TestClass());
actor.init(spy::consumeFlag);
// ...
}

关于java - Mockito Spy 看不到正确的原始值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62819445/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com