Java 假外呼集成测试

Java integration test with fake outbound call

我在一个 Java 项目上工作,使用 Spring 框架、JUnit 和 Mockito。

该应用程序与其他应用程序位于链的中间,因此它公开了 入站端口(例如 HTTP API)以供调用并使用 出站端口(例如网络服务和数据库)调用其他应用程序。

我想写类似的集成测试应该通过从入站端口到出站端口的整个java代码,但不做任何调用到项目之外的任何东西。

让我们举一个非常简单但非常具体的例子:

我们公开一个 HTTP 端点来获取客户,然后调用另一个应用程序来获取他们。

因此:

现在,我想通过调用 CustomerControllerAdaptergetCustomers() 来测试我的应用程序,这将调用 真实服务 ,后者将调用真正的供应商,它将调用一个虚假的存储库

我写了下面的代码:

@ExtendWith(SpringExtension.class)
class CustomerIntegrationTest {

    @Mock
    private CustomerRepository repository;

    @InjectMocks
    private CustomerControllerAdapter controller;

    @BeforeAll
    void setupAll() {
        CustomerOutboundPort customerOutboundPort = new CustomerSupplierAdapter(repository);
        CustomerInboundPort customerInboundPort = new CustomerService(customerOutboundPort);
        controller = new CustomerControllerAdapter(customerInboundPort);
    }

    @Test
    void bulkQuery() {
        // Given
        CustomerModel model = new CustomerModel();
        model.setName("Arya Stark");
        doReturn(List.of(model)).when(repository).getCustomers();

        // When
        List<CustomerDto> dtos = controller.getCustomers();

        // Then
        assertThat(dtos).hasSize(1);
        assertThat(dtos.get(0).getName()).isEqualTo("Arya Stark");
    }
    
}

但是在这段代码中,我在 setupAll() 中自己完成了“构造函数的连接”,而不是依赖 Spring 依赖注入。这不是一个可行的解决方案,因为它很难在现实环境中维护(控制器可能有多个服务,服务可能有多个供应商等)。

实际上,我想要类似的注解来设置CustomerRepository实例以以编程方式重载 依赖注入。喜欢:“嘿Spring,如果任何@Service class需要一个CustomerRepository那么你应该使用这个假的而不是通常的具体实现" 无需自己接线。

有什么方法可以使用 Spring、JUnit、Mockito 或其他任何东西来实现吗?

如果您真的想用模拟替换测试中的每个 CustomerRepository(无处不在!),我建议您使用提供 @Bean 的配置,它会创建一个模拟 bean。

@Profile("test")
@Configuration
public class TestConfiguration {
    @Bean
    @Primary
    public CustomerRepository customerRepostiory() {
        return Mockito.mock(CustomerRepository.class);
    }
}

@MockBean 可能会对您的测试持续时间产生负面影响,因为很可能 Spring 需要重新启动它的上下文。

或者,我建议根本不要模拟您的存储库,而是使用内存中的等效项 (H2) 或 TestContainers 框架来为您启动真实的数据库.在开始测试之前,您无需模拟,而是将数据插入存储库。