简体   繁体   English

Mockito:模拟具有多个构造函数的对象?

[英]Mockito: Mocking an object that has more than one constructor?

是否可以模拟一个可能在被测类中实例化的对象,该对象不使用依赖注入,并且将根据用于被测类的c-tor使用不同的构造函数实例化?

Your mocks don't actually invoke the constructor. 您的模拟实际上不会调用构造函数。 They invoke a proxy object of sorts, which will resolve to that object type but is injected with a lot of runtime utilities to allow you to inspect it. 他们调用某种代理对象,该代理对象将解析为该对象类型,但注入了许多运行时实用程序以允许您检查它。

If you want to test your constructors, then you would want to actually instantiate it without a mock. 如果测试构造函数,则实际上需要实例化而无需模拟。

Think of constructor invocations as the static calls that they are: If you call new Foo(...) in your code, Mockito will not be able to provide a replacement or mock Foo (at least without rewriting your system-under-test, which is what Powermock can do ). 将构造函数调用视为它们的静态调用:如果您在代码中调用了new Foo(...) ,则Mockito将无法提供替换或模拟Foo (至少在不重写被测系统的情况下, 这是Powermock可以做的 )。 This is true regardless of how many constructors Foo has. 不管Foo有多少个构造函数,这都是事实。

If you aren't allowed to change your system-under-test, you're stuck: It's just not a flexible system as it is. 如果不允许您更改被测系统,那么您将陷入困境:它实际上并不是一个灵活的系统。 This isn't necessarily a bad thing: If your system's dependency is lightweight and resilient, you might instead just make sure the dependency is well-tested and treat it as an unchangeable implementation detail of your system-under-test. 这并不一定是一件坏事:如果系统的依赖项是轻量级且具有弹性的,那么您可以仅确保已对依赖项进行了良好的测试,并将其视为被测系统的不可更改的实现细节。

If you want to replace your instance with a mock, you will probably need to insert a testing seam where you can get in and replace the instance, which is a sort of dependency injection. 如果要用模拟替换实例,则可能需要插入一个测试接缝 ,以便进入并替换该实例,这是一种依赖注入。 You might choose to do so with a factory, which can be kept as a field or passed in as a constructor argument: 您可能选择使用工厂来这样做,该工厂可以保留为字段或作为构造函数参数传递:

class YourSystemUnderTest {
  interface FooFactory {
    Foo create(Bar bar);           // one-arg constructor
    Foo create(Bar bar, Baz baz);  // two-arg constructor
  }

  class DefaultFooFactory implements FooFactory {
    @Override public Foo create(Bar bar) { return new Foo(bar); }
    @Override public Foo create(Bar bar, Baz baz) { return new Foo(bar, baz); }
  }

  /** Visible and non-final for testing. */
  FooFactory fooFactory = new DefaultFooFactory();  // replace this in tests

  // ...
}

Or, as a slightly more-hacky way to do this, delegate to an overridable method call in your system-under-test, and override that in your test. 或者,作为一种稍微有点怪异的方法,在被测系统中委派一个可重写的方法调用,然后在测试中覆盖它。

class YourSystemUnderTest {
  /** Visible and non-final for overriding. */
  Foo createFoo(Bar bar) { return new Foo(bar); }
  /** Visible and non-final for overriding. */
  Foo createFoo(Bar bar, Baz baz) { return new Foo(bar, baz); }
}

class YourSystemUnderTest_Test {
  @Mock Foo mockFoo;

  @Before void createSystemUnderTest() {
    yourSystemUnderTest = new YourSystemUnderTest() {
      @Override Foo createFoo(Bar bar) { return mockFoo; }
      @Override Foo createFoo(Bar bar, Baz baz) { return mockFoo; }
    };
  }

  // ...
}

Note that in both cases, you are accurately representing that YourSystemUnderTest is no longer to locked to only using a real Foo instance, but rather that it is allowed to use other implementations of Foo including test-double implementations. 请注意,在这两种情况下,您都准确地表示YourSystemUnderTest不再只限于使用真正的Foo实例,而是被允许使用Foo的其他实现,包括测试双重实现。 Outside of tests, this may come in handy if you ever upgrade or change your system-under-test's dependencies. 在测试之外,如果您升级或更改被测系统的依赖项,这可能会派上用场。

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

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