模拟一个自动装配的 spring 依赖项(接口),它不被测试 class 直接使用

Mock an autowired spring dependency (Interface) which is not directly used by the test class

问题(简而言之)- 我如何模拟一个 spring 在我的 JUnit 中没有直接引用的依赖项。

以下是正在使用的设置。 ServiceA 是一个接口,它的实现需要一些自动装配的依赖项才能使其许多逻辑正常工作。

public interface ServiceA{
    void invoke();
    ...10 more methods
}

@Service
public class ServiceAImpl implements ServiceA{
    @Autowired
    private ServiceB serviceB; 

    @Autowired
    private ServiceC serviceC;

    @Autowired
    private DAOA daoA;

    @Autowired
    private DAOB daoB;

    @Override
    public void invoke(arg1, arg2){
        // use above dependencies to do something
        serviceB.sendEmail(args);
    }
}

ServiceB 通过调用另一个实用程序依赖项来发送电子邮件。

public interface ServiceB{
    void sendEmail();
    ...10 more methods
}

@Service
public class ServiceBImpl implements ServiceB{
    @Autowired
    private EmailUtil emailUtil;

    @Override
    public void sendEmail(args){
    emailUtil.sendEmail(args);
}

值得注意的是 sendEmail 方法 returns void.

@Component
publIt'sclass EmailUtil{
    @Autowired
    private JavaMailSenderImpl javaMailSenderImpl;

    public void sendEmail(args){
    // send email using spring & other APIs
    }
}

我的集成测试 JUnit 如下所示。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-context.xml" })
public class ServiceIT{
    @Autowired
    private ServiceA serviceA;

    @Test
    public void testA(){
    // prepare data mock in H2 DB
    serviceA.invoke(arg1, arg2);
    // assertions
    }
}

问题(长)- 我正在使用 H2 作为模拟数据库,因此我的 DAO 调用可以(需要)起作用。我想模拟其他第 3 方集成,例如在 serviceB 中调用 emailUtil.sendEmail 以便不发送电子邮件。因为在我的 JUnit 中使用 serviceA,我的理解是,我需要创建 EmailUtil 的 @Mock 和 @InjectMocks 到 ServiceB 中,这是 @InjectMocks 到 ServiceA 中。当我这样做时,Mockito 会抛出错误,指出 ServiceB 是一个接口。创建 ServiceA、ServiceB 的模拟对象可能不是一个很好的选择,因为那样我可能不得不 stub/mock 太多行为。我希望在这些 类 中执行实际逻辑,模拟应该只对 sendEmail 方法进行。

  1. 这是一个常见问题吗?
  2. 我该如何解决这个问题?
  3. 这段代码的设计是否有问题(即使用 接口和自动装配一切),这使得很难 test/mock。如果是,什么是更好的方法?

Spring - 4.0.3.RELEASE,JUnit - 4.12,mockito-core - 1.10.19

在测试中 class ServiceIT mock EmailUtil using @MockBean

Annotation that can be used to add mocks to a Spring ApplicationContext. Can be used as a class level annotation or on fields in either @Configuration classes, or test classes that are @RunWith the SpringRunner.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-context.xml" })
public class ServiceIT{
@Autowired
private ServiceA serviceA;

@MockBean
private EmailUtil emailUtil;

@Test
public void testA(){
// prepare data mock in H2 DB

//given(this.emailUtil.sendEmail(ArgumentMatchers.any()).willReturn( custom object);
serviceA.invoke(arg1, arg2);
// assertions
     }
 }