这不是我的代码的样子,但它将接近我想要的例子
class Phone {
public makeCall(String number) {
addEntryToCallLog(number)
//eventually make a call
}
private void addEntryToCallLog(String number) {
//make database calls
}
}
class PhoneTest {
Phone phone
@Test
public void canMakeACall() {
//mock the behaviour of phone.addEntryToCallLog to prevent database exceptions
phone.makeCall("1223");
//Assert somehow that call was made << --IGNORE this NOT IMPORTANT
}
}
我想测试&#34; makeCall&#34;在单元测试中,但是当我这样做时,代码会抛出一些数据库异常,从而破坏了我的测试。 我认为能够测试模拟java私有方法的行为是合理的,因为这允许所有测试的一致行为。
我过去曾使用过groovy并且使用它可以使用spock来模拟私有方法的行为。我也可以使用metaClasses为我创建的实例做同样的事情。但在java中,似乎没有一种直接的方法来做到这一点。
我也尝试过mockito和power mockito,但是他们允许我更改私有方法的返回值,但是他们不允许我改变行为。
这似乎是一个显而易见的事情,那里有人已经处理过。我想我错过了一些东西。但它是什么。
答案 0 :(得分:2)
我的建议是放宽从private
到包私有的可见性,可能带有@VisibleForTesting
annotation或/** Visible for testing. */
注释。此时,您可以使用doAnswer
“部分模拟”您的内部方法,正如DTing在另一个答案中提到的那样,以替换实际实现的行为。
这里的主要问题是Mockito最擅长将接口转换为存根实现。然而,私有方法非常故意不是接口的一部分,因此Java通过Mockito所依赖的一些反射特性使它们harder to access。 (在幕后,Mockito正在创建你正在嘲笑的类的子类,而子类通常不能覆盖私有方法。)
您还应该记住,理想情况下,您的单元测试应该将您的类作为一个单元进行测试,而无需深入研究实现细节。通过说你试图替换你的私有方法的行为,你发出你的类需要一个尚不存在的接缝的信号。您可以考虑重构依赖注入:
class Phone {
interface CallLogger {
void addEntry(String number);
}
private final CallLogger logger;
public Phone() {
this(new DefaultCallLogger());
}
/** Visible for testing. */
Phone(CallLogger logger) {
this.logger = logger;
}
/* ... */
}
...然后可以让你选择你想要的任何CallLogger,包括在生产中进入数据库,在测试中伪造或嘲笑它,或者在将来用批量版本替换它。同样地,使addEntryToCallLog
包私有或protected
:您指示子类可以更改他们向调用日志添加条目的方式,这正是您在测试中尝试做的事情,并且承认这将有助于Java VM和您的代码的读者了解行为可以在哪些方面发生变化。
所有这一切,如果您只是想使用PowerMockito来替换实现,您可以使用PowerMockito的doAnswer
和when
为您的私有方法提供不同的行为。请记住,您有机会改进设计,花费更少的时间和精力来对抗实施细节。
答案 1 :(得分:1)
您的示例很奇怪,因为您的getContactFromDatabase不会返回任何内容,只是您使用它来设置Contact变量。如果你想模拟对象创建行为,请看看:
https://code.google.com/p/mockito/wiki/MockingObjectCreation
class PhoneTest {
@Spy Phone phone;
@Test
public void canMakeCall() {
doReturn(someContact)
.when(phone)
.getContactFromDatabase(someString);
phone.makeCall(someString);
}
}
如果你想什么都不做:
class PhoneTest {
@Spy Phone phone;
@Test
public void canMakeCall() {
doNothing()
.when(phone)
.getContactFromDatabase(someString);
phone.makeCall(someString);
}
}
http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#13
您可以创建真实对象的间谍。当你使用间谍然后 调用实际方法(除非方法被存根)。真正的间谍 应谨慎使用,偶尔使用,例如在交易时 遗留代码。