Java 用Mockito截获真正的非静态方法调用

Java 用Mockito截获真正的非静态方法调用,java,mocking,mockito,powermockito,intercept,Java,Mocking,Mockito,Powermockito,Intercept,是否有任何方法可以使用Mockito或PowerMockito,拦截对对象的非静态方法的调用,或者至少是对单例对象的调用 以下类别提供了一个示例: public class Singleton { private static Singleton INSTANCE = null; private Singleton(Object parameter) {} public static Singleton getInstance(Object parameter) { i

是否有任何方法可以使用
Mockito
PowerMockito
,拦截对对象的非静态方法的调用,或者至少是对单例对象的调用

以下类别提供了一个示例:

public class Singleton {

  private static Singleton INSTANCE = null;

  private Singleton(Object parameter) {}

  public static Singleton getInstance(Object parameter) {
    if (INSTANCE == null) {
      INSTANCE = new Singleton(parameter);
    }
    return INSTANCE;
  }

  public String process(String a, String b) {
    return (a + b);
  }

  // Other methods
}

public class Foreign {

  private Foreign() {}

  public static void main(String[] args) {
    System.out.println(Singleton.getInstance(new Object()).process("alpha", "beta"));
  }
}
Singleton
对象是在
Foreign
类中创建的,不受某些测试代码(上面未显示)的控制。这两个类都不能修改。目标是拦截对测试代码中非静态
process()
方法的调用,以便对于某些值,返回不同的结果,例如调用

Singleton.getInstance(new Object()).process("alpha", "beta");
mock返回
“alpha-beta”
,而不是预期的
“alpha-beta”

一种解决方案可能是拦截
Singleton.getInstance()
方法来实例化Singleton的自定义子类,例如使用

public class SubSingleton extends Singleton {

  public SubSingleton(Object parameter) {
    super(parameter);
  }

  public String process(String a, String b) {
    if ("alpha".equals(a) && "beta".equals(b)) {
      return a + "-" + b;
    }
    return super.process(a + b);
  }
}
然后,对
Singleton.process()
方法的调用将被拦截,如下所示:

Object parameter = new Object();
PowerMockito.doReturn(new SubSingleton(parameter)).when(Singleton.class, "getInstance", parameter);
但是,上面的
Singleton
类只提供了一个私有构造函数,因此不能对其进行扩展。使用
PowerMockito.whenNew()
返回部分模拟(spy)也不起作用,因为
Singleton
类不提供无参数构造函数


所需的模拟能否以任何其他方式实现?可以对非单例类执行此操作吗?

首先,可以对具有某些参数的构造函数的对象使用whenNew:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {

    @Mock
    Singleton singletonMock;

    @Before
    public void setUp() throws Exception {
        PowerMockito.whenNew(Singleton.class)
                .withAnyArguments()
                .thenReturn(singletonMock);
    }

    @Test
    public void testMockNew() throws Exception {
        Mockito.when(singletonMock.process(anyString(), anyString())).thenReturn("sasa");
        Foreign.main(new String[0]);
    }
}
其次,为什么不使用stub getInstance而不是new:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {

    @Test
    public void testMockNew() {
        PowerMockito.mockStatic(Singleton.class);
        Singleton singletonMock = Mockito.mock(Singleton.class);
        PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
        Mockito.when(singletonMock.process(anyString(), anyString())).thenReturn("sasa");
        Foreign.main(new String[0]);
    }
}
第三,拦截处理方法:

  • 创造真正的单身
  • 创建一个模拟单例
  • 模拟静态getInstance以返回模拟。注意:在获得真实实例后,必须调用mockStatic
  • 使用Answer检查
    process
    call上的参数
    • 如果符合所需模式,则返回所需答案
    • else在实单例上调用实方法
最后,用间谍代替模拟

@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {

    @Test
    public void testMockNew() {
        var singletonReal = Singleton.getInstance(new Object());
        var singletonMock = Mockito.spy(singletonReal);
        PowerMockito.mockStatic(Singleton.class);
        PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
        Mockito.when(singletonMock.process("alpha", "beta")).thenReturn("sasa");
        // NOTE: real method is called for other args
        Foreign.main(new String[0]);
    }
}


第一个方法不提供解决方案,因为它拦截模拟对象的
process()
,而不是singleton,它实际上是由
Foreign
和其他类调用
singleton
类的其他方法使用的。这同样适用于第二种方法,因为
Singleton
类也有其他方法(在问题中阐明)。第三种方法应该适用于单例,因为间谍活动发生在真实对象上。是否适用于非单身人士+谢谢你的回答。我的回答给出了解决方案的各个阶段。第一个例子表明,当构造函数有参数时,可以使用whenNew,您认为这是不可能的。2.如果还有其他方法,并且您只想截获一些调用,那么第三个和第四个代码段就是最好的选择(我的首选是第四个)。3.我对
Singleton.getInstance(对象参数)
非常困惑,它接受一个参数,如果Singleton已经存在,就会忽略它。4.适用于非单身人士-我需要一个具体的例子。谢谢你的回答,+1并被接受。我评论的意思是,
whenNew()
没有提供解决方案,因为它提供的是模拟对象,而不是真正的单例。2.根据我之前的评论,已于4日同意。3.代码是一个简化的示例,参数可以是任何东西,也可以有多个。4.外部类使用的任何应用程序,只要存在多个实例。如果传递模拟参数,则带有参数的私有构造函数并不总是可用的,因为对实际方法的调用可能不会产生有效的结果。
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {

    @Test
    public void testMockNew() {
        var singletonReal = Singleton.getInstance(new Object());
        var singletonMock = Mockito.spy(singletonReal);
        PowerMockito.mockStatic(Singleton.class);
        PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
        Mockito.when(singletonMock.process("alpha", "beta")).thenReturn("sasa");
        // NOTE: real method is called for other args
        Foreign.main(new String[0]);
    }
}