简体   繁体   English

如何在Java的代码流中模拟一个中间类

[英]How to mock an intermediate class in the code flow in Java

I have 3 classes, as shown below, and I want to write a Unit test for the validate() method of class A .我有 3 个类,如下所示,我想为类Avalidate()方法编写一个单元测试。 I do not want to mock class B .我不想嘲笑B类。 I want to mock class C and always return the same String when getDetails() is called.我想模拟C类并在调用getDetails()时始终返回相同的String Is this possible with Mockito? Mockito 可以做到这一点吗? If not, is there any alternative?如果没有,还有其他选择吗?

Class A{
  
  void validate(){
    B b = new B();
    b.verify();
  }
}

Class B throws Exception{
  void verify(){
    
    C c = new C();
    c.getDetails();
  }
}

Class C{
  String getDetails(){
    //Does an API call and returns the part of response as a string
  }
}

You can use PowerMock for this.您可以为此使用 PowerMock。 See the following example:请参见以下示例:

@RunWith(PowerMockRunner.class)
@PrepareForTest(C.class)
public class Test {

  @Test
  public void testMockingC() throws Exception {
    PowerMockito.stub(PowerMockito.method(C.class, "getDetails")).toReturn("Mocked successfully!");
    A a = new A();
    a.validate();
  }
}

Since no method returns the relevant value I couldn't do some assertion, but you can put a breakpont and track the program flow while running this test, c.getDetails() will return Mocked successfully!由于没有方法返回相关值,我无法进行断言,但您可以在运行此测试时放置一个断点并跟踪程序流程, c.getDetails()Mocked successfully! . .

B doesn't seem to let you pass in a C , so it's hard to substitute the C used with a test double. B似乎不允许您通过C ,因此很难用测试替身代替C使用的。 The problem is that B knows all the details of C 's construction even when it doesn't (and shouldn't) care.问题是B知道C构造的所有细节,即使它不(也不应该)关心。 Dependency injection is an important pattern.依赖注入是一个重要的模式。

The idea is to first introduce an abstraction, of which you can have different implementations:这个想法是首先引入一个抽象,你可以有不同的实现:

interface DetailsProvider {
    public String getDetails();
}

There will be one implementation that makes the real API call, used in the production code and another that's completely in your control, for the tests.将有一个实现进行真正的 API 调用,在生产代码中使用,另一个实现完全在您的控制中,用于测试。

Then, you make B take a DetailsProvider as a constructor parameter.然后,让BDetailsProvider作为构造函数参数。 The beauty in this is that it now no longer knows how the instance is created, making it easy to supply different versions of it.这样做的美妙之处在于它现在不再知道实例是如何创建的,从而可以轻松地提供它的不同版本。 Anything implementing the interface will do.任何实现接口的东西都可以。

class B {
   public B(DetailsProvider detailsProvider) {
       this.detailsProvider = detailsProvider;
   }
    public void verify () {
        String details = detailsProvider.getDetails();
        ...
    }

   private DetailsProvider detailsProvider;
}

For testing, we can have a fake:为了测试,我们可以有一个假的:

class FakeDetailsProvider implements DetailsProvider {
    @Override
    String getDetails() { 
        return "foo"; 
    }
}

so somewhere in your test setup, you have, eg所以在你的测试设置的某个地方,你有,例如

B someB = new B(new FakeDetailsProvider());

In the production code, C now needs to implement the interface:在生产代码中, C现在需要实现接口:

class C implements DetailsProvider {
    @Override
    public String getDetails() {
        // Make the real API call.
    }
}

and again, you just wire the two up:再次,您只需将两者连接起来:

B myB = new B(new C());

Note that this may not compile;请注意,这可能无法编译; I've not written Java for a while now!我已经有一段时间没有写 Java 了!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM