在下面的代码中,我预计given
将抛出MissingMethodInvocationException
,因为foo()
是最终的。
但是我在str.equals("String 1")
得到NullPointerException
。
Mockito调用的是真实代码。为什么?
import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
class Foo {
private String str = "String 1";
public final String foo() {
if (str.equals("String 1")) {
str = "String 2";
}
return str;
}
}
@RunWith(MockitoJUnitRunner.class)
public class TestClass {
@Mock
Foo foo;
@Test
public void test() {
given(foo.foo()).willReturn("x");
assertEquals("x", foo.foo());
}
}
在下面的例子中,我删除了if子句。现在它可以正常工作了。当然,我不想删除这些行,因为它们在我正在测试的代码中是需要的。
这些台词对Mockito的行为有什么影响?
public final String foo() {
return str;
}
我怎么能确保Mockito永远不会调用真正的代码上的方法,即使他们碰巧是最终的?
我宁愿看MissingMethodInvocationException
。
在这段代码中,测试通过了:
public String foo() {
if (str.equals("String 1")) {
str = "String 2";
}
return str;
}
我问的原因是,我有一个测试用例,有人添加了final
修饰符被测试/模拟的方法之一。
我们没有看到MissingMethodInvocationException
,而是看到了一些不相关的异常,这些异常是从模拟方法内部的"真实"代码抛出的。我们花了一些时间寻找导致测试失败的地方和更改。
如果Mockito抛出MissingMethodInvocationException
,我们会立即看到原因。
tl;dr: Mockito不能模拟final方法。而且它也不能检测到正在调用的final方法。
更长的解释:这是Java中final的缺点之一。唯一的选择是使用Powermock,尽管我将保留对遗留代码的使用。
Mockito的问题是,它通过子类化要模拟的类型来工作,Java编译器或JVM不允许任何继承final类或重写final方法的东西。Mockito不能在我们目前的设计上做任何事情。
而Powermock添加了另一个步骤,在另一个类加载器中重新加载类,执行一些修改,特别是删除要模拟的类型中的final标志。尽管这种方法也有缺点:测试中的配置更多,测试消耗更多的Permgen,测试启动速度较慢。可以使用PowerMock
参见Powermockito可以模拟非final具体类中的final方法吗?