如何给微服务架构的项目做验收测试?

本文介绍了在微服务架构下进行验收测试所面临的挑战,并提出验收测试应具备自动化、可重复和易于集成CI工具的特性。通过一个Demo展示了如何利用Docker、Cucumber、Byteman和Fabric8等工具,实现对商品服务和商品价格服务的自动化测试,特别是在模拟异常情况下的测试策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在验收测试阶段,基于微服务架构的应用相对于单体架构的应用而言,具有以下挑战:

  1. 复数的服务增加了测试环境搭建的难度

  2. 各种异常情况的模拟变得困难,基于 Mock 的测试方式无法对整个调用链路作异常模拟,进而使得对整体架构的健壮性测试变得困难

  3. 基于成本和效率的原因,测试工作已经不适合通过人工完成

我们认为对基于微服务架构的应用,其验收测试应该具有以下特征:

  1. 自动化、可重复、易于集成CI工具

  2. 能够在测试运行时修改服务的行为

下面我们通过一个 Demo 来介绍如何利用 DockerCucumberBytemanFabric8 docker-maven-pluginSpotify dockerfile-maven-plugin达成以上目标。

 

01 Demo 实践

一共有两个服务:

  • Product Service(商品服务)
  • Product Price Service(商品价格服务)

Product Service 提供了一个查询接口用于获得商品信息及其价格信息的组合结果,这相当于跨服务的 SQL JOIN。

Product 的 Schema:

{

  "id": "<string>",

  "name": "<string>",

  "description": "<string>"

}

ProductPrice的Schema:

{

  "id": "<string>",

  "price": "<number>"

}

Product Service返回的Schema则是:

{

  "products": [

    {

      "id": "<string>",

      "name": "<string>",

      "description": "<string>",

      "price": <number>

    }

  ]

}

该接口的实现逻辑是:

  1. Product Service 本地查询得到 Product List

  2. Product Service 调用 Product Price Service 接口得到 ProductPrice List

  3. 拼装结果

此外还有一个要求,当 Product Price Service 出现异常时,Product Service 依然要能够返回结果,只不过 price 字段为 null,即无论如何 Product Service 都要能够返回结果。

 

02 实现步骤

构建 Docker Image

为了能够便利地搭建测试环境,我们需要先为Product Servcie和Product Price Service构建 Docker Image。利用 Spotify dockerfile-maven-plugin 可以很方便地做到这一点,它没有引入额外的概念,只要你会写 Dockerfile 就行。我们在 Product Service 和 Producer Price Service 的 pom.xml 中添加类似以下的配置:

<plugin>

  <groupId>com.spotify</groupId>

  <artifactId>dockerfile-maven-plugin</artifactId>

  <version>${dockerfile-maven-plugin.version}</version>

  <configuration>

    <repository>msat-${project.artifactId}</repository>

    <tag>${project.version}</tag>

    <buildArgs>

      <JAR_FILE>${project.build.finalName}-exec.${project.packaging}</JAR_FILE>

    </buildArgs>

  </configuration>

  <executions>

    <execution>

      <id>build</id>

      <phase>package</phase>

      <goals>

        <goal>build</goal>

      </goals>

    </execution>

  </executions>

</plugin>

并且提供了 Dockerfile:

FROM openjdk:8-jre-alpine

ARG JAR_FILE

ENV JAR_FILE=${JAR_FILE}

RUN mkdir /maven

COPY target/${JAR_FILE} /maven

COPY target/lib/byteman.jar /maven

ENTRYPOINT java $JAVA_OPTS -jar /maven/$JAR_FILE

EXPOSE 8080

 

注意我们在 Image 中添加了 byteman.jar,利用它我们可以在运行时修改程序。

编写验收测试脚本

我们新建一个验收测试的Maven项目,然后使用 Cucumber 编写了以下两个场景的验收测试脚本:

正常情况:

Feature: List product information with price



  Scenario: Everything is good

    Given Product Service is up and running

    And Product Price Service is up and running



    When User query product list



    Then Get following products



      | id       | name | description            | price |

      | animal-1 | dog  | woof woof              | 1000  |

      | animal-2 | duck | quack quack            | 40    |

      | animal-3 | fox  | what does the fox say? | 5000  |

 

这个脚本的大致意思是在 Product Service 和 Product Price Service 都启动的情况下,当用户查询 Product 信息时,我们会得到上述表格中的结果。

Product Price Service 异常情况:

Feature: List product information with price



  Scenario: Product Price Service throws exception when being queried

    Given Product Service is up and running

    And Product Price Service is up and running



    Given Install the byteman script product_price_exception.btm to Product Price Service



    When User query product list



    Then Get following products



      | id       | name | description            | price |

      | animal-1 | dog  | woof woof              |       |

      | animal-2 | duck | quack quack            |       |

      | animal-3 | fox  | what does the fox say? |       |

 

我们在这里使用了 Byteman 给 Product Price Service 注入了异常情况:

Given Install the byteman script product_price_exception.btm to Product Price Service

product_price_exception.btm 的内容是这样的:

RULE throw exception

CLASS me.chanjar.msat.productprice.FakeProductPriceRepository

METHOD listAll

AT ENTRY

IF TRUE

DO debug("throw RuntimeException here"),

   throw new RuntimeException("Product Repository Error!")

ENDRULE

意思是在调用 FakeProductPriceRepository.listAll 方法时抛出异常,注意这样做并没有修改 Product Price Service 的源码,而是在运行时修改了它的逻辑。

接下来我们为上面的验收测试脚本实现逻辑(下面代码与实际上有所不同,这是为了尽量使得代码篇幅精简):

public class Stepdefs {



  private List<Map<String, String>> answer;



  @Given("^Product Service is up and running$")

  public void productServiceIsUpAndRunning() {

    probe("Product Service", PRODUCT_ADDRESS);

  }



  @And("^Product Price Service is up and running$")

  public void productPriceServiceIsUpAndRunning() {

    probe("Product Price Service", PRODUCT_PRICE_ADDRESS);

    clearBytemanScript();

  }



  @When("^User query product list$")

  public void queryProductList() {

    answer = given()

      .when()

      .get(PRODUCT_ADDRESS + "/products")

      .then()

      .statusCode(is(200))

      .extract()

      .body()

      .jsonPath()

      .getList("products", Map.class);

  }



  @Given("^Install the byteman script ([A-Za-z0-9_\\.]+) to Product Price Service$")

  public void injectExceptionIntoProductPriceService(String bytemanScript) throws Exception {

    injectBytemanScript("target/test-classes/" + bytemanScript);

  }



  @Then("^Get following products$")

  public void compareResult(List<Map<String, String>> expected) {

    assertThat(answer).containsExactlyInAnyOrderElementsOf(expected);

  }

}

关于 Cucumber 和 Byteman 的更详细的介绍可以见 ServiceComb Saga 使用 Cucumber 做验收测试源码分析

自动化搭建测试环境

我们希望能够在 Maven 的 integration-test 阶段搭建测试环境、执行上述验收测试脚本。在 pom.xml 中添加到 Fabric8 docker-maven-plugin

<plugin>

  <groupId>io.fabric8</groupId>

  <artifactId>docker-maven-plugin</artifactId>

  <configuration>

    <showLogs>true</showLogs>

    <images>

      <image>

        <name>msat-product:${project.version}</name>

        <alias>msat-product</alias>

        <run>

          <wait>

            <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log>

            <time>120000</time>

          </wait>

          <links>

            <link>msat-product-price:msat-product-price</link>

          </links>

          <ports>

            <port>product.port:8080</port>

          </ports>

        </run>

      </image>

      <image>

        <name>msat-product-price:${project.version}</name>

        <alias>msat-product-price</alias>

        <run>

          <env>

            <JAVA_OPTS>

              -Dorg.jboss.byteman.debug=true -Dorg.jboss.byteman.verbose=true

              -javaagent:/maven/byteman.jar=port:9091,address:0.0.0.0,listener:true

            </JAVA_OPTS>

          </env>

          <wait>

            <log>Started [a-zA-Z]+ in [0-9.]+ seconds</log>

            <time>120000</time>

          </wait>

          <ports>

            <port>product-price.port:8080</port>

            <port>product-price.byteman.port:9091</port>

          </ports>

        </run>

      </image>

    </images>

  </configuration>

  <executions>

    <execution>

      <id>start</id>

      <phase>pre-integration-test</phase>

      <goals>

        <goal>start</goal>

      </goals>

    </execution>

    <execution>

      <id>stop</id>

      <phase>post-integration-test</phase>

      <goals>

        <goal>stop</goal>

      </goals>

    </execution>

  </executions>

</plugin>

 

这样一来就能够在 pre-integration-test 阶段启动容器,也能在 post-integration-test 阶段销毁容器了。

Take a run

接下来只需要 mvn clean install 它就会:

  1. 构建:

    1. 构建 Product Service 项目,并为其构建 Docker Image

    2. 构建 Product Price Service 项目,并为其构建 Docker Image

  2. 验收测试:

    1. 启动 Product Service 和 Product Price Service 的容器

    2. 执行验收测试脚本

    3. 销毁上述创建的容器

如果你想自己试试可以下载本项目源码

欢迎开发者朋友们加入 ServiceComb 社区,一起做些有意思的事情。加入Servicecomb社区 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值