Java 如何注入同一接口的多个模拟
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21054057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to inject multiple mocks of the same interface
提问by shuttsy
The Java class (called ServiceCaller
) I wish to test has this:
ServiceCaller
我希望测试的 Java 类(称为)具有以下功能:
@Autowired @Qualifier(value="serviceA")
SomeService serviceA;
@Autowired @Qualifier(value="serviceB")
SomeService serviceB;
(there's a doWork()
method that will check a condition and call either A or B).
(有一种doWork()
方法可以检查条件并调用 A 或 B)。
How do I inject a mock of each service into the appropriate variable?
如何将每个服务的模拟注入适当的变量?
My Junithas this:
我的Junit有这个:
@InjectMocks ServiceCaller classUnderTest = new ServiceCaller();
@Mock SomeService mockServiceA;
@Mock SomeService mockServiceB;
Yet when I run my tests to check that service A/B called under the correct condition, I get null pointers as the mock hasn't been injected.
然而,当我运行测试以检查在正确条件下调用的服务 A/B 时,由于尚未注入模拟,我得到了空指针。
Obviously its because of multiple dependencies on the same interface (SomeService
). Is there a way to specify the qualifier when declaring the mock service? Or do I need to have setters for the dependencies and set the old fashioned way?
显然这是因为对同一个接口 ( SomeService
)的多个依赖。有没有办法在声明模拟服务时指定限定符?或者我是否需要为依赖项设置设置器并设置老式方式?
采纳答案by Marcin Zaj?czkowski
It should be enough to name your mocks serviceA and serviceB. From Mockito documentation:
将您的模拟命名为 serviceA 和 serviceB 就足够了。从 Mockito文档:
Property setter injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the property name and the mock name.
属性设置器注入;mocks 将首先按类型解析,然后,如果有多个相同类型的属性,则通过属性名称和模拟名称的匹配。
In your example:
在你的例子中:
@InjectMocks ServiceCaller classUnderTest;
@Mock SomeService serviceA;
@Mock SomeService serviceB;
Note that it is not necessary to manually create class instance when using @InjectMocks.
请注意,使用@InjectMocks 时无需手动创建类实例。
Nevertheless I personally prefer injecting dependencies using constructor. It makes it easier to inject mocks in tests (just call a constructor with your mocks - without reflections tools or @InjectMocks
(which is useful, but hides some aspects)). In addition using TDDit is clearly visible what dependencies are needed for the tested class and also IDE can generate your constructor stubs.
不过我个人更喜欢使用构造函数注入依赖项。它可以更轻松地在测试中注入模拟(只需使用模拟调用构造函数 - 无需反射工具或@InjectMocks
(这很有用,但隐藏了某些方面))。除了使用TDD 之外,还可以清楚地看到测试类需要哪些依赖项,IDE 还可以生成构造函数存根。
Spring Framework completely supports constructor injection:
Spring Framework 完全支持构造函数注入:
@Bean
public class ServiceCaller {
private final SomeService serviceA;
private final SomeService serviceB;
@Autowired
public ServiceCaller(@Qualifier("serviceA") SomeService serviceA,
@Qualifier("serviceB") SomeService serviceB) { ... }
...
}
This code can be tested with:
可以使用以下代码测试此代码:
@Mock SomeService serviceA;
@Mock SomeService serviceB;
//in a setup or test method
ServiceCaller classUnderTest = new ServiceCaller(serviceA, serviceB);
回答by osiris256
you can use "name" property to define your instance like this:
您可以使用“name”属性来定义您的实例,如下所示:
@Mock(name="serviceA") SomeService serviceA;
@Mock(name="serviceB") SomeService serviceB;
回答by Abhijeet Ranade
When you have same type dependencies mockito stops injecting the depedencies due to properties of same types. To resolve this with reference to @osiris256 in the following way:
当您具有相同类型的依赖项时,由于相同类型的属性,mockito 停止注入依赖项。要通过以下方式参考@osiris256 解决此问题:
class ServiceLayer{
@Autowired
@Qualifier("bean1")
private InterfaceA typeA;
@Autowired
@Qualifier("bean2")
private InterfaceA typeB;
}
Your test class should be:
你的测试类应该是:
@RunWith(SpringRunner.class)
class ServiceLayerTest{
@Mock(name = "typeA")
private InterfaceA typeA;
@Mock(name = "typeB")
private InterfaceA typeB;
@InjectMocks
ServiceLayer serviceLayer;
@Before
public void initialiseBeforeTest(){
MockitoAnnotations.initMocks(this);
}
// here goes your test
@Test
public void test(){
// use your when then .....
}
}
Note: if you are using SpringRunner and use @MockBean this will not work, you have to replace with @Mock(name="") with reference to @osiris256.
注意:如果您正在使用 SpringRunner 并使用 @MockBean 这将不起作用,您必须参考@osiris256 替换为@Mock(name="")。