[英]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.