基于SpringBoot为微服务架构编写端到端测试
本文来源:http://r6d.cn/HwyV
微服务架构的一个主要方面是应用程序形成为松散耦合的服务的集合,每个服务可以独立地部署并且通过某种轻型协议相互通信。
现在假设您要为Cart Service编写端到端测试。您很快就会发现它并不容易,让我列举一些原因:
购物车服务需要知道如何启动定价服务,目录服务和MongoDB(如果您想要涉及前端以及Coolstore GW和WebUI)。 购物车服务需要为两种外部服务准备一些数据(固定装置)。 您使用网络与服务进行通信。可能会发生一些测试失败,不是因为真正的故障,而是因为基础设施问题或其他服务有任何错误。因此,这些测试的可能性变得不稳定并且开始失败,因为当前服务中引入的任何更改都更高。 在更复杂的情况下,在成本(部署到云),时间(启动所有基础架构和服务)和维护时间方面,运行这些测试可能会很昂贵。 很难在开发人员计算机中运行它们,因为您需要在计算机上安装所有部件。
因此,端到端测试不是测试微服务的最佳方法,但您仍需要一种从服务的开始到结束进行测试的方法。
有必要找到一种“模拟”这些外部依赖关系的方法,而不必注入任何模拟对象。我们需要做的是欺骗被测服务,因此它确实认为它正在与真实的外部服务进行通信,而实际上并非如此。
允许我们这样做的方法是Service Virtualiztion。服务虚拟化是一种模拟组件应用程序(如基于API)的行为的方法。
您可以将服务虚拟化视为您过去在OOP中实现的模拟方法,而不是在对象级别进行模拟,而是在服务级别进行模拟。这是对企业的嘲弄。
有很多服务虚拟化工具,但根据我的经验,在JVM生态系统中,更好的工具之一是Hoverfly。
让我们看看Cart Service的“端到端”测试是怎样的。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "CATALOG_ENDPOINT=catalog")
public class CartServiceBoundaryTest {
@Autowired
private TestRestTemplate restTemplate;
@ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(dsl(
service("catalog")
.get("/api/products")
.willReturn(success(json(ProductsObjectMother.createVehicleProducts())))
));
@Test
public void should_add_item_to_shopping_cart() {
final ShoppingCart shoppingCart = this.restTemplate.postForObject("/api/cart/1/1111/2", "", ShoppingCart.class);
assertThat(shoppingCart)
.returns(0.0, ShoppingCart::getCartItemPromoSavings)
.returns(2000.0, ShoppingCart::getCartItemTotal)
.returns(-10.99, ShoppingCart::getShippingPromoSavings)
.returns(2000.0, ShoppingCart::getCartTotal)
.extracting(ShoppingCart::getShoppingCartItemList)
.hasSize(1);
}
}
这个服务是使用Spring Boot实现的,所以我们使用的是Spring Boot Test框架。这里的重要部分是使用CATALOG_ENDPOINT属性指定部署Catalog服务的URL 。对于此测试,它设置为目录。
下一个重点是Hoverfly类规则部分。在该规则中,指定了以下内容:
在测试之前启动HTTP代理,并将来自JVM的所有传出流量重定向到该代理。 它记录了当完成对主机目录的请求并且路径是 /api/products时,它必须返回给定json文档的成功结果。
测试本身只使用TestRestTemplate(它是一个休息客户端)并验证您可以向购物车添加一些元素。
请注意,您无需配置启动HTTP代理的位置或配置任何端口,因为Hoverfly会自动配置JVM网络参数,以便任何网络通信都通过Hoverfly代理。
请注意,现在您不需要知道如何启动Catalog服务,也不需要知道如何使用正确的数据对其进行配置。
您正在其边界内测试整个服务,从传入消息到传出消息到其他服务,而不模拟任何内部元素。
您可能想知道“如果当前服务还依赖于数据库服务器会发生什么?”
在这种情况下,您什么也不做,因为服务本身知道正在使用哪个数据库服务器以及它需要的数据类型,您只需要启动数据库服务器,填充所需的数据(夹具)并执行测试。对于这种情况,我建议您使用Arquillian Cube Docker从Docker容器启动数据库服务,这样您就不需要在需要运行测试的每台机器上安装它,而Arquillian Persistence Extension则用于将数据库维护到已知状态。
在下一个评级服务示例中,您可以简要了解如何将它们用于持久性测试:
public class ApueCubeRatingServiceTest {
// Starts in local dockerhost (docker machine or native) mongo docker image before running the test class
@ClassRule
public static ContainerDslRule mongodbContainer = new ContainerDslRule("mongo:3.2.18-jessie")
.withPortBinding(27017);
//Defines APE (Arquillian Persistence Extension to work as rule)
@Rule
public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule();
// Defines to use MongoDb as NoSql Populator
@MongoDb
@ArquillianResource
NoSqlPopulator populator;
@Test
public void should_calculate_average_rating_when_adding_an_already_inserted_item() {
createPopulatorConfiguration()
.usingDataSet("single_rating_with_double.json")
.execute();
// Execute test
}
@After
public void tearDown() {
createPopulatorConfiguration().clean();
}
private NoSqlPopulatorConfigurator createPopulatorConfiguration() {
return populator.forServer(
mongodbContainer.getIpAddress(),
mongodbContainer.getBindPort(27017))
.withStorage(TEST_DATABASE);
}
}
通过这种方法,您可以确保服务的所有内部组件按预期工作,并避免微服务中端到端测试的片状性质。
因此,任何微服务中的端到端测试与整体应用程序中的端到端测试并不完全相同; 您仍在测试整个服务,但保持受控环境,其中测试仅依赖于服务边界内的组件。
合同测试如何适应?那么,这里显示的所有内容都可以用于合同测试的消费者和提供者方面,以避免启动任何外部服务。通过这种方式,正如许多作者所总结的那样,如果您使用合同测试,这些将成为新的端到端测试。
原文标题《Writing End to End Tests for a Microservices Architecture》
作者:Alex Soto
译者:February