接口测试之幂等性闲谈

何为幂等性

幂等(idempotent)或幂等性(idempotence)源自数学。在抽象代数中,有一整套理论上是关于幂等的。幂等的数学定义,远远超出网络上所谓的:"如果满足f(x)=f(f(x)),那么f就是幂等的"。

在计算机领域,幂等有一个十分通俗的定义:其实幂等就是一个操作或者接口,不管你调多少次,每次执行的结果都跟第一次一样,那么这个操作就是幂等的。幂等的对立面是非幂等。在非幂等操作中,执行多次的效果与执行一次的效果不同。

幂等性的重要性

对于一些重要的接口或者操作,我们是要求后台保证其幂等性的。因为客户端可能有重试机制,另外中间人攻击可能会进行请求的重放,这些都有可能导致接口被多次调用,如扣款操作,如果接口没有实现幂等,那么后果是很严重的。

如何实现幂等性

  • MVCC: 多版本并发控制,乐观锁的一种实现,在数据更新时需要去比较持有数据的版本号,版本号不一致的操作无法成功;

  • 数据库唯一索引: 利用数据库表单的特性来实现幂等,常用的一个思路是在表上构建唯一性索引,保证某一类数据一旦执行完毕,后续同样的请求再也无法成功写入。 比如博客上面要想防止一个人重复点赞,可以设计一张表,将博客id与用户id绑定建立唯一索引,每当用户点赞时就往表中写入一条数据,这样重复点赞的数据就无法写入。

  • TOKEN机制: 这种机制就比较重要了,适用范围较广,有多种不同的实现方式。其核心思想是为每一次操作生成一个唯一性的凭证,也就是token。一个token在操作的每一个阶段只有一次执行权,一旦执行成功则保存执行结果。对重复的请求,返回同一个结果。以电商平台为例子,电商平台上的订单id就是最适合的token。当用户下单时,会经历多个环节,比如生成订单,减库存,减优惠券等等。每一个环节执行时都先检测一下该订单id是否已经执行过这一步骤,对未执行的请求,执行操作并缓存结果,而对已经执行过的id,则直接返回之前的执行结果,不做任何操作。这样可以在最大程度上避免操作的重复执行问题,缓存起来的执行结果也能用于事务的控制等。

  • 请求标识:在请求中添加唯一标识符(例如UUID),并在服务端记录已处理的标识符,如果收到重复请求,则可以直接返回之前的响应。

幂等性与接口测试

接口级的幂等性测试

首先我们在测试之前要清楚接口是否具有幂等性。如查询指定资源,删除指定资源,以相同的方式修改指定资源,创建指定ID的资源,这些接口都应当是幂等的。

接口级的幂等性测试是在接口测试中非常重要的一种测试方式,用于确保同一个请求被多次执行后,得到的结果应该是一致的。下面是进行接口级的幂等性测试时需要注意的几点:

  1. 制定测试计划:在进行接口幂等性测试之前,需要制定测试计划,包括测试场景、测试数据、测试用例等,以确保测试工作能够有条不紊地进行。

  2. 准备测试数据:测试数据需要包括正常情况下的数据和异常情况下的数据,以覆盖接口的所有可能使用场景。为了保证测试数据可重复使用,可以使用mock数据或者自动生成测试数据。

  3. 编写测试用例:编写测试用例需要依据接口的参数、状态、返回值、逻辑等考虑,包括模拟发送多个相同请求、不同但等效的请求以及重复执行已经执行过的请求等方式进行测试。

  4. 执行测试用例:在执行测试用例时,需要注意测试环境的稳定性,确保每次测试都在相同的环境下进行,并且测试用例之间相互独立。同时,需要对每个测试用例的执行结果进行记录与分析。

  5. 分析测试结果:在测试完成后,需要对测试结果进行分析,查看测试用例是否符合预期,确定接口是否具有幂等性。如果测试结果不符合预期,需要对接口进行优化。

通常我们很难只通过调用单个接口,来校验这个接口是否以幂等地方式实现。毕竟,单个接口的能力有限,可能无法让我们获取服务端的资源状态变化情况。这时,我们往往需要调用多个接口来校验。例如,要测试删除接口的幂等性,可以通过连续调用删除 -> 查询 -> 删除 -> 再查询四个接口来实现。

业务级的幂等性测试

业务级的幂等性测试是针对具体业务场景的测试。由于不同的业务场景中,对接口幂等性的要求不同,因此有些业务场景下需要特别关注幂等性这一点,需要进行业务级的幂等性测试。下面是进行业务级的幂等性测试时需要注意的几点:

  1. 确定测试场景:由于不同的业务场景中,对接口幂等性的要求不同,因此需要根据实际业务场景来确定需要测试哪些接口以及如何进行测试。

  2. 准备测试数据:测试数据需要包括正常情况下的数据和异常情况下的数据,并且需要考虑业务流程的各个阶段,以保证真实模拟业务流程。

  3. 编写测试用例:编写测试用例需要从业务角度出发,根据实际业务场景中的各种情况,制定相应的测试用例,包括利用多个用户同时请求、同一个用户多次请求等方式测试接口的幂等性,确保在多种情况下接口都能够正确处理请求。

  4. 执行测试用例:在执行测试用例时,需要模拟真实的业务场景,并且需要对每个测试用例的执行结果进行记录与分析。

  5. 分析测试结果:在测试完成后,需要对测试结果进行分析,查看测试用例是否符合预期,确定接口是否具有幂等性。如果测试结果不符合预期,需要对接口进行优化。

接口测试自身的幂等性

测试用例多次执行,与单次执行的效果一样。这里的效果一样是指用例多次执行后的被测系统状态与一次执行后、甚至没有执行时相同。

这种观点其实与我们所强调的用例独立性不谋而合。用例不只有测试步骤,还有SETUP和TEARDOWN。在TEARDOWN中,尤其要做好资源的释放和环境的清理,从而避免对当前用例的再次执行或者其他用例的执行产生影响。

设计测试用例去验证接口的幂等性的举例说明

案例一:

假设有一个添加商品至购物车的接口 /api/cart/add_product,接口需要传入参数 product_id, quantityuser_id。在该业务场景中,我们需要保证该接口具有幂等性,即当同一用户对同一商品进行多次添加操作时,产生的结果应该是一致的。

下面是一些可能的测试用例:

  1. 测试用例1:使用同一个用户对同一商品进行两次添加操作,每次添加一件商品。期望结果:第一次操作返回成功,购物车中的商品数量为1;第二次操作也返回成功,但购物车中的商品数量仍为1,即不会出现重复添加的情况。

  2. 测试用例2:使用同一用户对同一商品进行两次添加操作,第一次添加一件商品,第二次添加两件商品。期望结果:第一次操作返回成功,购物车中的商品数量为1;第二次操作也返回成功,且购物车中的商品数量变为3,即能正确地处理多次添加操作。

  3. 测试用例3:使用不同的用户对同一商品进行添加操作,每个用户都添加一件商品。期望结果:每次添加操作都返回成功,购物车中的商品数量为2,能正确地区分不同用户对同一商品的添加操作。

在实际测试中,需要设计更多的测试用例来覆盖更多的场景,包括不同的用户、不同的商品数量、异常情况等。同时,在执行测试过程中,需要确保每个测试用例之间的独立性,以便准确地验证接口的幂等性。

案例二:

假设有一个名为“更新订单”的接口,可以通过HTTP PUT方法来更新一个订单。该接口的请求和响应格式如下:

请求:

PUT /orders/123456
{
  "customer": "John",
  "items": [
    {
      "id": "001",
      "name": "product1",
      "quantity": 2
    },
    {
      "id": "002",
      "name": "product2",
      "quantity": 1
    }
  ],
  "total": 100
}

响应:

HTTP/1.1 200 OK
{
  "id": "123456",
  "customer": "John",
  "items": [
    {
      "id": "001",
      "name": "product1",
      "quantity": 2
    },
    {
      "id": "002",
      "name": "product2",
      "quantity": 1
    }
  ],
  "total": 100
}

接下来,设计测试用例去验证接口的幂等性:

  1. 第一次请求 发送一个PUT请求来更新一个订单,并记录响应中的id字段。

  2. 第二次请求 使用相同的请求参数发送一个PUT请求,将id字段设置为第一次请求中记录的id值,并记录响应中的id字段。

  3. 验证响应结果 比较第一次和第二次请求的响应结果是否相同。如果响应结果相同,则说明接口具有幂等性。 需要注意的是,在测试用例中需要保证每次请求的参数和请求头都是相同的,只有请求体中的id字段和其他字段不同。这样可以确保测试用例的准确性和可重复性。 如果需要进一步验证接口的幂等性,可以增加以下测试用例:

  4. 使用不同的请求参数发送一次PUT请求,验证响应结果与第一次请求的响应结果不同。

  5. 发送一次DELETE请求删除订单,再使用相同的请求参数发送一次PUT请求,验证响应结果与第一次请求的响应结果不同。 以上是一个设计测试用例验证接口幂等性的示例,可以根据具体的接口和业务场景进行调整和扩展。