testable-mock icon indicating copy to clipboard operation
testable-mock copied to clipboard

直接调用的方法被Mock后不生效,间接调用的生效

Open wushifeng opened this issue 2 years ago • 6 comments

版本:  <testable.version>0.7.7</testable.version>

被测试类 public class MockDemo { public String callCommonFunc() { return commonFunc(); } public String commonFunc(){ return MockDemo.class.getSimpleName(); } }

测试类 @EnablePrivateAccess class MockDemoTest { @MockDiagnose(LogLevel.VERBOSE) public static class Mock { // @MockInvoke(targetClass = MockDemo.class, targetMethod = "commonFunc") // public String commonFunc() { // return "mock"; // } @MockInvoke public String commonFunc(MockDemo self) { System.out.println(self); return "mock"; } @MockInvoke(targetClass = MockDemo.class) public String callCommonFunc() { return "mock"; } } @Test public void mock(){ MockDemo u = new MockDemo(); assertEquals("mock", u.callCommonFunc()); assertEquals("mock", u.commonFunc()); } }

错误日志 [DIAGNOSE] Found source class io/MockDemo [VERBOSE] Found method [VERBOSE] Line 10, constructing "java.lang.Object()" [VERBOSE] Found method callCommonFunc [VERBOSE] Line 12, invoking "io.MockDemo::commonFunc() : java.lang.String" [VERBOSE] Line 12, mock method "commonFunc" used [VERBOSE] Line 12, invoking "io.MockDemoTest$Mock::commonFunc(io.MockDemo) : java.lang.String" [VERBOSE] Found method commonFunc [VERBOSE] Line 16, invoking "java.lang.Class::getSimpleName() : java.lang.String" [DIAGNOSE] Found mock class io/MockDemoTest$Mock [VERBOSE] Mock method "commonFunc" as "Lio.MockDemo;::commonFunc() : java.lang.String" [VERBOSE] Mock method "callCommonFunc" as "io.MockDemo::callCommonFunc() : java.lang.String" [DIAGNOSE] Found 2 mock methods io.MockDemo@3d3e5463

org.opentest4j.AssertionFailedError: Expected :mock Actual :MockDemo

wushifeng avatar Jun 28 '22 08:06 wushifeng

Mock只对业务类中的调用生效,因为Mock的目的本来就是在测试的时候,需要在不直接修改业务类代码的前提下,绕过业务类代码中的外部调用(或其他会影响测试的调用)。

对于测试方法里直接发起的调用,不应当被Mock。

linfan avatar Jul 08 '22 03:07 linfan

是这个场景 **业务类有两个方法,**如:MockDemo callCommonFunc commonFunc public String callCommonFunc() { return commonFunc(); } public String commonFunc(){ return MockDemo.class.getSimpleName(); }

现在想mock上面两个方法: 类似如下的配置 @MockInvoke(targetClass = MockDemo.class, targetMethod = "commonFunc") @MockInvoke(targetClass = MockDemo.class) public String callCommonFunc()

commonFunc生效了,而callCommonFunc不生效

wushifeng avatar Jul 08 '22 09:07 wushifeng

首先Testable Mock的方法的调用,而不是方法本身。举例来说,假设原本代码是:

public String callCommonFunc() {
    return commonFunc();    <--- 这个是调用,在测试运行的时候会被自动替换
}
public String commonFunc() {      <--- 这个方法本身是不会被改动的
    return MockDemo.class.getSimpleName();
}

然后在同一个Mock方法上,只支持一个@MockInvoke注解,如果有两个@MockInvoke的话,只有第一个会生效(这里的第一个是指生成字节码后的第一个,和源码里的注解顺序可能不一致)。因此因为targetMethod参数的作用,上述代码实际上就只Mock了commonFunc方法。

linfan avatar Jul 08 '22 09:07 linfan

刚才贴的少了2行,看初始内容,是mock了两个方法,写的方式如下 @MockInvoke public String commonFunc(MockDemo self) { System.out.println(self); return "mock"; } @MockInvoke public String commonFunc(MockDemo self) { System.out.println(self); return "mock"; }

然后测试时,commonFunc不生效 public void mock(){ MockDemo u = new MockDemo(); assertEquals("mock", u.callCommonFunc()); assertEquals("mock", u.commonFunc()); }

wushifeng avatar Jul 08 '22 09:07 wushifeng

理解了系统的设计方式,https://alibaba.github.io/testable-mock/#/zh-cn/doc/design-and-mechanism 划重点:Mock的目标是被测类中的方法调用。测试用例里的代码不会被Mock,方法的定义本身没有变化,只是发起调用的代码被替换了

这种场景如何来做:

class Demo{
  public String test(){
      ...
      this.innerFunc();
     ...
   return ....;
  }
}

假设我想mock: new Demo().test() 这个方法,那需要对代码逻辑进行修改才能mock,不然只是方法内的调用可以被mock,那就只能mock这个函数innerFunc

这种情况对一些已有的系统的测试就有些麻烦,不知有没有方式来支持这个场景 没深入testable-mock的代码,如果这种场景不支持,具体是什么原因

wushifeng avatar Jul 14 '22 08:07 wushifeng

是啊,这种直接的对被测试的方法进行mock,难道还要打桩吗

huanghe1993 avatar Jun 02 '23 07:06 huanghe1993