Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 构造函数的mockito单元测试_Java_Unit Testing_Mockito - Fatal编程技术网

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();
 }