如何注入相同接口的多个模拟

shu*_*tsy 19 java spring dependency-injection mocking mockito

ServiceCaller我想测试的Java类(调用)有这个:

@Autowired @Qualifier(value="serviceA")
SomeService serviceA;

@Autowired @Qualifier(value="serviceB")
SomeService serviceB;
Run Code Online (Sandbox Code Playgroud)

(有一种doWork()方法可以检查条件并调用A或B).

如何将每个服务的模拟注入适当的变量?

我的Junit有这个:

@InjectMocks ServiceCaller classUnderTest = new ServiceCaller();

@Mock SomeService mockServiceA;
@Mock SomeService mockServiceB;
Run Code Online (Sandbox Code Playgroud)

然而,当我运行我的测试以检查在正确条件下调用的服务A/B时,我得到空指针,因为没有注入模拟.

显然它是因为对同一个接口的多重依赖(SomeService).有没有办法在声明模拟服务时指定限定符?或者我是否需要为依赖项设置setter并设置旧的方式?

Mar*_*ski 22

为mocks serviceA和serviceB命名应该足够了.来自Mockito 文档:

物业安装人员注入; mocks将首先按类型解析,然后,如果存在多个相同类型的属性,则通过属性名称和模拟名称的匹配来解析.

在你的例子中:

@InjectMocks ServiceCaller classUnderTest;

@Mock SomeService serviceA;
@Mock SomeService serviceB;
Run Code Online (Sandbox Code Playgroud)

请注意,使用@InjectMocks时无需手动创建类实例.

不过我个人更喜欢使用构造函数注入依赖项.它可以更容易地在测试中注入模拟(只需用你的模拟调用构造函数 - 没有反射工具或@InjectMocks(这很有用,但隐藏了一些方面)).此外,使用TDD可以清楚地看到测试类需要哪些依赖项,IDE也可以生成构造函数存根.

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) { ... }

    ...
}
Run Code Online (Sandbox Code Playgroud)

可以使用以下代码测试此代码:

@Mock SomeService serviceA;
@Mock SomeService serviceB;

//in a setup or test method
ServiceCaller classUnderTest = new ServiceCaller(serviceA, serviceB); 
Run Code Online (Sandbox Code Playgroud)


小智 8

您可以使用"name"属性来定义您的实例,如下所示:

@Mock(name="serviceA") SomeService serviceA;
@Mock(name="serviceB") SomeService serviceB;
Run Code Online (Sandbox Code Playgroud)


小智 5

当您具有相同类型的依赖项时,mockito 由于具有相同类型的属性而停止注入依赖项。要通过以下方式参考 @osiris256 解决此问题:

class ServiceLayer{

   @Autowired
   @Qualifier("bean1")
   private InterfaceA typeA;  

   @Autowired
   @Qualifier("bean2")
   private InterfaceA typeB;  

}
Run Code Online (Sandbox Code Playgroud)

你的测试类应该是:

@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 .....
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您使用 SpringRunner 并使用 @MockBean 这将不起作用,您必须参考 @osiris256 替换为 @Mock(name="") 。