Java 假外呼集成测试
Java integration test with fake outbound call
我在一个 Java 项目上工作,使用 Spring 框架、JUnit 和 Mockito。
该应用程序与其他应用程序位于链的中间,因此它公开了 入站端口(例如 HTTP API)以供调用并使用 出站端口(例如网络服务和数据库)调用其他应用程序。
我想写类似的集成测试应该通过从入站端口到出站端口的整个java代码,但不做任何调用到项目之外的任何东西。
让我们举一个非常简单但非常具体的例子:
我们公开一个 HTTP 端点来获取客户,然后调用另一个应用程序来获取他们。
- 在 域中 :客户由
Customer
class. 代表
- 在 externalapp 层中:客户由
CustomerModel
class. 表示
- 在 rest 层中:客户由
CustomerDto
class. 表示
因此:
-
CustomerSupplierAdapter
class 从 CustomerRepository
获取数据并进行从 CustomerModel
到 Customer
的映射。
-
CustomerControllerAdapter
class 执行从 Customer
到 CustomerDto
和 returns 数据的映射。
现在,我想通过调用 CustomerControllerAdapter
的 getCustomers()
来测试我的应用程序,这将调用 真实服务 ,后者将调用真正的供应商,它将调用一个虚假的存储库。
我写了下面的代码:
@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 框架来为您启动真实的数据库.在开始测试之前,您无需模拟,而是将数据插入存储库。
我在一个 Java 项目上工作,使用 Spring 框架、JUnit 和 Mockito。
该应用程序与其他应用程序位于链的中间,因此它公开了 入站端口(例如 HTTP API)以供调用并使用 出站端口(例如网络服务和数据库)调用其他应用程序。
我想写类似的集成测试应该通过从入站端口到出站端口的整个java代码,但不做任何调用到项目之外的任何东西。
让我们举一个非常简单但非常具体的例子:
我们公开一个 HTTP 端点来获取客户,然后调用另一个应用程序来获取他们。
- 在 域中 :客户由
Customer
class. 代表
- 在 externalapp 层中:客户由
CustomerModel
class. 表示
- 在 rest 层中:客户由
CustomerDto
class. 表示
因此:
-
CustomerSupplierAdapter
class 从CustomerRepository
获取数据并进行从CustomerModel
到Customer
的映射。 -
CustomerControllerAdapter
class 执行从Customer
到CustomerDto
和 returns 数据的映射。
现在,我想通过调用 CustomerControllerAdapter
的 getCustomers()
来测试我的应用程序,这将调用 真实服务 ,后者将调用真正的供应商,它将调用一个虚假的存储库。
我写了下面的代码:
@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 框架来为您启动真实的数据库.在开始测试之前,您无需模拟,而是将数据插入存储库。