Java 构造函数的mockito单元测试
我有一节课Java 构造函数的mockito单元测试,java,unit-testing,mockito,Java,Unit Testing,Mockito,我有一节课 Class First { private Second second; public First(int num, String str) { second = new Second(str); this.num = num; } ... // some other methods } 我想先为类的公共方法编写单元测试。我想避免执行第二类的构造函数 我这样做: Second second = Mockito.m
Class First {
private Second second;
public First(int num, String str) {
second = new Second(str);
this.num = num;
}
... // some other methods
}
我想先为类的公共方法编写单元测试。我想避免执行第二类的构造函数
我这样做:
Second second = Mockito.mock(Second.class);
Mockito.when(new Second(any(String.class))).thenReturn(null);
First first = new First(null, null);
它仍然在调用第二类的构造函数。如何避免它?单元测试的问题再次来自使用
new
操作符手动创建对象。考虑传递已经创建的代码>第二版/代码>:
class First {
private Second second;
public First(int num, Second second) {
this.second = second;
this.num = num;
}
// some other methods...
}
我知道这可能意味着对API进行重大重写,但没有其他方法。而且这个类没有任何意义:
Mockito.when(new Second(any(String.class).thenReturn(null)));
首先,Mockito只能模拟方法,不能模拟构造函数。其次,即使您可以模拟构造函数,您也在模拟刚创建的对象的构造函数,并且从未真正对该对象做过任何事情。您可以使用
但是重新分解是更好的决定。我使用了“模式2-工厂助手模式”
模式2-factory辅助模式
这种模式不起作用的一种情况是MyClass是final。大多数Mockito框架对final类的使用都不太好;这包括spy()的使用。另一种情况是MyClass使用getClass()这不起作用,因为间谍的类实际上是原始类的Mockito生成的子类
在这两种情况下,您都需要更健壮的factory helper模式,如下所示
public class MyClass{
static class FactoryHelper{
Foo makeFoo( A a, B b, C c ){
return new Foo( a, b, c );
}
}
//...
private FactoryHelper helper;
public MyClass( X x, Y y ){
this( x, y, new FactoryHelper());
}
MyClass( X x, Y, y, FactoryHelper helper ){
//...
this.helper = helper;
}
//...
Foo foo = helper.makeFoo( a, b, c );
}
因此,您有一个专门用于测试的特殊构造函数,它有一个额外的参数。这是在创建要测试的对象时从测试类中使用的。在测试类中,您模拟FactoryHelper类以及要创建的对象
@Mock private MyClass.FactoryHelper mockFactoryHelper;
@Mock private Foo mockFoo;
private MyClass toTest;
你可以这样使用它
toTest = new MyClass( x, y, mockFactoryHelper );
when( mockFactoryHelper.makeFoo(
any( A.class ), any( B.class ), any( C.class )))
.thenReturn( mockFoo );
来源:以下是使用PowerMockitoAPI模拟此功能的代码
Second mockedSecond = PowerMockito.mock(Second.class);
PowerMockito.whenNew(Second.class).withNoArguments().thenReturn(mockedSecond);
您需要使用Powermockito runner并需要添加所需的测试类(逗号分隔),这些测试类需要由powermock API模拟
@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class,Second.class})
class TestClassName{
// your testing code
}
我相信,使用mockito模拟构造函数是不可能的
Class First {
private Second second;
public First(int num, String str) {
if(second== null)
{
//when junit runs, you get the mocked object(not null), hence don't
//initialize
second = new Second(str);
}
this.num = num;
}
... // some other methods
}
以及,对于测试:
class TestFirst{
@InjectMock
First first;//inject mock the real testable class
@Mock
Second second
testMethod(){
//now you can play around with any method of the Second class using its
//mocked object(second),like:
when(second.getSomething(String.class)).thenReturn(null);
}
}
将这一行放在测试类的顶部
@PrepareForTest({ First.class })
Mockito现在可以模拟构造函数(从3.5.0版开始)
可能重复,但还有另一种方法:将PowerMock添加到混合中,这样您就可以模拟
第二个构造函数,正如@terma的答案所示。@Rogério我同意也不同意。我同意这是一个可行的替代方案,但我不同意,因为OP只使用Mockito,没有显示出任何愿望,甚至没有能力B获得并使用一个单独的框架,这在公司环境中比在学术环境中更为琐碎。根据OP对其问题的措辞,这应该仍然是公认的答案。此外,Tomasz在回答中提出的第一点,Mockito是为了促进编写可测试的代码。使用Powermock表明您已经编写了不稳定的代码,需要重构。@searchengine27事实上,没有问题说添加另一个测试库会有问题(当然可能有问题)根据我在实际项目中的经验,添加测试库并不是一个大问题,因为它只用于自动测试,而不用于生产代码。Mockito并不是“为了促进编写可测试的代码”“-这只是你的意见,没有证据;如果你认为有,请给我一份推荐信。相反,使用带有这种任意(和偶然)限制的测试库通常会妨碍正确的API设计和OO代码。他可以将对象创建逻辑移动到一个方法中,并模拟/覆盖该方法进行测试。因此,如果在构造函数中有一个实例化Second
的方法调用,那么如何模拟呢?我也使用了模式2,但我认为该文档中有一个重要的输入错误。请参阅我对的评论。我实现了工厂模式,它解决了我的另一个问题。我实现了这个模式。但是现在,我一直在讨论如何为工厂类编写测试用例。覆盖率注意:如果被测试的类(A)是正在创建另一个类(B)的新实例的类(B),那么在这种情况下,您必须将A添加到“@PrepareForTest”以调用whenNew(),这将阻止Jacoco读取覆盖率。这是有效的模式吗?声纳将在初始化前抱怨进入场Second
。如果它不是最终的,这可以避免,但它仍然是一个推荐的模式吗?
@PrepareForTest({ First.class })
try (MockedConstruction mocked = mockConstruction(Foo.class)) {
Foo foo = new Foo();
when(foo.method()).thenReturn("bar");
assertEquals("bar", foo.method());
verify(foo).method();
}