这是班级:
public class A {
public final SqlOperator op;
public final ImmutableList<RexNode> operands;
public final RelDataType type;
protected A(RelDataType type,SqlOperator op,List<? extends RexNode> operands) {
assert type != null : "precondition: type != null";
assert op != null : "precondition: op != null";
assert operands != null : "precondition: operands != null";
this.type = type;
this.op = op;
this.operands = ImmutableList.copyOf(operands);
assert op.getKind() != null : op;
assert op.validRexOperands(operands.size(), Litmus.THROW) : this;
}
我想模拟A类,但将“operands”字段的值设置为空列表。该字段是final我在模拟类时无法在构造函数外修改它。
我尝试使用Reflection,但这与Mockito无关。
需要测试用例的代码:
public static String extractGranularity(A call) {
if (call.getKind() != SqlKind.FLOOR || call.getOperands().size() != 2) {
return null;
}
final B flag = (B) call.operands.get(1); // This is the problem area
final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue();
if (timeUnit == null) {
return null;
}
return timeUnitSwitch(timeUnit);
}
答案 0 :(得分:0)
好的,所以这里有一些基于你发布的课程的内容(我认为实际的课程有点不同):
package so45078998;
import com.google.common.collect.ImmutableList;
import java.util.List;
public class A {
public final SqlOperator op;
public final ImmutableList<RexNode> operands;
public final RelDataType type;
protected A(RelDataType type, SqlOperator op, List<? extends RexNode> operands) {
assert type != null : "precondition: type != null";
assert op != null : "precondition: op != null";
assert operands != null : "precondition: operands != null";
this.type = type;
this.op = op;
this.operands = ImmutableList.copyOf(operands);
assert op.getKind() != null : op;
assert op.validRexOperands(operands.size(), Litmus.THROW) : this;
}
public Object getKind() {
return op.getKind();
}
public ImmutableList<RexNode> getOperands() {
return operands;
}
static class SqlOperator{
private Object kind;
public Object getKind() {
return kind;
}
void setKind(final Object kind) {
this.kind = kind;
}
boolean validRexOperands(int size, Litmus litmus) {
return true;
}
}
static class RexNode{
private Object value;
public Object getValue() {
return value;
}
void setValue(final Object value) {
this.value = value;
}
}
static class RelDataType{}
static enum Litmus{ THROW }
}
// ------------------------------------------------------
package so45078998;
public class ToTest {
public static String extractGranularity(A call) {
if (call.getKind() != SqlKind.FLOOR || call.getOperands().size() != 2) {
return null;
}
final B flag = (B) call.operands.get(1); // This is the problem area
final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue();
if (timeUnit == null) {
return null;
}
return timeUnitSwitch(timeUnit);
}
private static String timeUnitSwitch(final TimeUnitRange timeUnit) {
return "OK";
}
enum SqlKind { FLOOR, OTHER }
static class TimeUnitRange {}
static class B extends A.RexNode {}
}
测试类(这两个测试都是绿色):
package so45078998;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class ToTestTest {
@Test
public void testExtractGranularityWithKindNotFloor() throws Exception {
ToTest.B rexNode1 = new ToTest.B();
ToTest.B rexNode2 = new ToTest.B();
List<A.RexNode> operands = new ArrayList<>();
operands.add(rexNode1);
operands.add(rexNode2);
final A.RelDataType relDataType = new A.RelDataType();
final A.SqlOperator sqlOperator = new A.SqlOperator();
sqlOperator.setKind(ToTest.SqlKind.OTHER);
A a = new A(relDataType, sqlOperator, operands);
Assert.assertNull(ToTest.extractGranularity(a));
}
@Test
public void testExtractGranularity() throws Exception {
ToTest.B rexNode1 = new ToTest.B();
ToTest.B rexNode2 = new ToTest.B();
rexNode2.setValue(new ToTest.TimeUnitRange());
List<A.RexNode> operands = new ArrayList<>();
operands.add(rexNode1);
operands.add(rexNode2);
final A.RelDataType relDataType = new A.RelDataType();
final A.SqlOperator sqlOperator = new A.SqlOperator();
sqlOperator.setKind(ToTest.SqlKind.FLOOR);
A a = new A(relDataType, sqlOperator, operands);
Assert.assertEquals("OK", ToTest.extractGranularity(a));
}
}
这是有效的,因为测试类和测试类共享相同的包名(因此您可以直接调用new A(...)
)。
否如果您想要抑制某些A行为,请使用间谍并在需要时使用它来控制行为。
我希望有所帮助。
答案 1 :(得分:0)
也许你可以尝试使用Unsafe进行黑客攻击:
sun.misc.Unsafe unsafe = null;
try {
final PrivilegedExceptionAction<sun.misc.Unsafe> action = () -> {
Field unsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafe.setAccessible(true);
return (sun.misc.Unsafe) unsafe.get(null);
};
unsafe = AccessController.doPrivileged(action);
} catch (Exception e) {
throw new RuntimeException("Unable to load unsafe", e);
}
long operandsOffset = unsafe.objectFieldOffset(
A.class.getDeclaredField("operands"));
A instance = // your instance of A
ImmutableList<RexNode> yourOverridingList = ...
unsafe.getAndSetObject(instance, operandsOffset, yourOverridingList);
答案 2 :(得分:-1)
封装,你这个笨蛋!!!
封装是隐藏类的实际实现细节。所以在你的情况下,你现在使用myA.operands
来访问实际的操作数。这可能会带来一些麻烦。不要公开所有属性,而是将它们private
添加,并添加一些访问方法:
public class A {
private final ImmutableList<RexNode> operands;
// rest of attributes and constructor...
public ImmutableList<RexNode> getOperands() {
return operands;
}
}
不仅你会遵循良好的做法,而且嘲笑它会更容易:
A myA = mock(A.class);
Mockito.when(myA.getOperands())
.thenReturn(/* what you want it to return */);