使用模拟测试私有方法


public class A {
    public void method(boolean b){
          if (b == true)
               method1();
          else
               method2();
    }
    private void method1() {}
    private void method2() {}
}
public class TestA {
    @Test
    public void testMethod() {
      A a = mock(A.class);
      a.method(true);
      //how to test like    verify(a).method1();
    }
}
如何测试私有方法

是否调用,以及如何使用mockito测试私有方法?

不可能通过 mockito。来自他们的维基

为什么Mockito不嘲笑私人方法?

首先,我们并不教条地嘲笑私人方法。我们只是 不关心私有方法,因为从 测试私有方法不存在。这里有几个原因 Mockito不嘲笑私有方法:

需要对类加载器进行黑客攻击,这永远不是防弹的,而且它 更改 API(必须使用自定义测试运行程序、批注类、 等(。

这很容易解决 - 只需更改方法的可见性 从专用到包保护(或受保护(。

需要我花时间实现和维护它。而它 鉴于第 #2 点以及它已经存在的事实,这是没有意义的 在不同的工具(PowerMock(中实现。

最后。。。嘲笑私有方法是暗示有东西 对面向对象的理解是错误的。在 OO 中,您希望对象(或角色(能够 协作,而不是方法。忘记pascal和程序代码。想 在对象中。

你不能用Mockito做到这一点,但你可以使用Powermock来扩展Mockito和模拟私有方法。Powermock支持Mockito。下面是一个示例。

这是一个如何使用 powermock 进行操作的小示例

public class Hello {
    private Hello obj;
    private Integer method1(Long id) {
        return id + 10;
    }
} 

要测试方法 1,请使用代码:

Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));

要设置私有对象 obj,请使用以下命令:

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);
虽然 Mockito

不提供该功能,但您可以使用 Mockito + JUnit ReflectionUtils 类或 Spring ReflectionTestUtils 类获得相同的结果。请参阅下面从此处获取的示例,说明如何调用私有方法:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ComTurretaSpringReflectionReflectiontestutilsApplicationTests {
    @Test
    public void test01()
    {
        Student student = new Student();
        student.setName("James Dean");
        System.out.println("Before: " + student.toString());
        ReflectionTestUtils.setField(student, "internalCode", 1343243);
        System.out.println("After: " + student.toString());
        ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");
    }
}

ReflectionTestUtils和Mockito的完整示例可以在Mockito for Spring一书中找到。

官方文档 春季测试

  1. 通过使用反射,可以从测试类调用私有方法。在这种情况下,

    测试方法将是这样的...

    public class TestA {
      @Test
        public void testMethod() {
        A a= new A();
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        // invoke the private method for test
        privateMethod.invoke(A, null);
        }
    }
    
  2. 如果私有方法
  3. 调用任何其他私有方法,那么我们需要监视对象并存根另一个方法。测试类将像...

    测试方法将是这样的...

    public class TestA {
      @Test
        public void testMethod() {
        A a= new A();
        A spyA = spy(a);
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        doReturn("Test").when(spyA, "method2"); // if private method2 is returning string data
        // invoke the private method for test
        privateMethod.invoke(spyA , null);
        }
    }
    

**方法是结合反射和监视对象。方法 1 和方法 2 是私有方法,方法 1 调用方法 2。

从行为的角度考虑这个问题,而不是从有什么方法的角度来考虑这个问题。 如果b为真,则称为method的方法具有特定的行为。 如果b为假,则它具有不同的行为。 这意味着您应该为 method 编写两个不同的测试;每种情况一个。 因此,您有两个面向行为的测试,而不是三个面向方法的测试(一个用于method,一个用于method1,一个用于method2

与此相关(我最近在另一个 SO 线程中建议了这个,结果被称为一个四个字母的单词,所以请随意对此持保留态度(;我发现选择反映我正在测试的行为的测试名称而不是方法的名称很有帮助。 所以不要把你的测试称为testMethod()testMethod1()testMethod2()等等。 我喜欢像calculatedPriceIsBasePricePlusTax()taxIsExcludedWhenExcludeIsTrue()这样表明我正在测试的行为的名称;然后在每种测试方法中,仅测试指示的行为。 大多数此类行为仅涉及对公共方法的一次调用,但可能涉及对私有方法的多次调用。

希望这有帮助。

我能够使用反射使用 mockito 在里面测试一个私有方法。这是一个例子,试图命名它,使其有意义

//Service containing the mock method is injected with mockObjects
@InjectMocks
private ServiceContainingPrivateMethod serviceContainingPrivateMethod;
//Using reflection to change accessibility of the private method
Class<?>[] params = new Class<?>[]{PrivateMethodParameterOne.class, PrivateMethodParameterTwo.class};
    Method m = serviceContainingPrivateMethod .getClass().getDeclaredMethod("privateMethod", params);
    //making private method accessible
    m.setAccessible(true); 
    assertNotNull(m.invoke(serviceContainingPrivateMethod, privateMethodParameterOne, privateMethodParameterTwo).equals(null));

你不应该测试私有方法。 只有非私有方法需要测试,因为这些方法无论如何都应该调用私有方法。 如果你"想要"测试私有方法,这可能表明你需要重新考虑你的设计:

我是否使用了正确的依赖注入?我是否需要将私有方法移动到一个单独的类中并对其进行测试?这些方法必须是私有的吗? ...它们不能是默认的或受保护的吗?

在上面的例子中,被称为"随机"的两种方法实际上可能需要放在它们自己的类中,进行测试,然后注入到上面的类中。

实际上有一种方法可以使用 Mockito 测试来自私有成员的方法。假设您有这样的类:

public class A {
    private SomeOtherClass someOtherClass;
    A() {
        someOtherClass = new SomeOtherClass();
    }
    public void method(boolean b){
        if (b == true)
            someOtherClass.method1();
        else
            someOtherClass.method2();
    }
}
public class SomeOtherClass {
    public void method1() {}
    public void method2() {}
}

如果你想测试a.method会从SomeOtherClass调用一个方法,你可以写下面这样的东西。

@Test
public void testPrivateMemberMethodCalled() {
    A a = new A();
    SomeOtherClass someOtherClass = Mockito.spy(new SomeOtherClass());
    ReflectionTestUtils.setField( a, "someOtherClass", someOtherClass);
    a.method( true );
    Mockito.verify( someOtherClass, Mockito.times( 1 ) ).method1();
}

ReflectionTestUtils.setField();会用您可以监视的东西来存根私人成员。

我真的不明白您需要测试私有方法。根本问题是您的公共方法具有 void 作为返回类型,因此您无法测试您的公共方法。因此,您被迫测试您的私有方法。我的猜测正确吗??

一些可能的解决方案(AFAIK(:

  1. 嘲笑你的私人方法,但你仍然不会"实际"测试你的方法。

  2. 验证方法中使用的对象的状态。大多数方法要么对输入值进行一些处理并返回输出,要么更改对象的状态。也可以使用测试对象是否处于所需状态。

    public class A{
    SomeClass classObj = null;
    public void publicMethod(){
       privateMethod();
    }
    private void privateMethod(){
         classObj = new SomeClass();
    }
    }
    

    [在这里你可以通过检查 classObj 的状态从 null 到 not null 来测试私有方法。

  3. 稍微重构一下你的代码(希望这不是遗留代码(。我编写方法的基本原则是,应该始终返回一些东西(int/布尔值(。返回的值可能或可能不会被实现使用,但它肯定会被测试使用

    法典。

    public class A
    { 
        public int method(boolean b)
        {
              int nReturn = 0;
              if (b == true)
                   nReturn = method1();
              else
                   nReturn = method2();
        }
        private int method1() {}
        private int method2() {}
    }
    

把你的测试放在同一个包中,但一个不同的源文件夹(src/main/java vs. src/test/java(,并使这些方法成为包私有的。Imo 可测试性比隐私更重要。

如果私有方法不为 void,并且返回值用作外部依赖项方法的参数,则可以模拟依赖项并使用ArgumentCaptor来捕获返回值。例如:

ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");

建立在@aravind-yarram的答案之上:不可能通过mockito。来自他们的维基

那么测试私有方法的OO方式是什么?具有复杂逻辑的私有方法可能表明您的类违反了单一责任原则,并且某些逻辑应移动到新类。

实际上,通过将这些私有方法提取到更精细类的公共方法中,您可以在不破坏原始类封装的情况下对它们进行单元测试。

相关内容

  • 没有找到相关文章