当前位置:首页 > 技术分析 > 正文内容

软件测试中数据验证的3大难题,这个工具搞定!

ruisui883个月前 (02-04)技术分析17

应用场景:

随着系统开发日益模块化和分布式部署服务器的普及,测试过程中常常需要验证与后端服务之间频繁数据交换的完整性。然而,实际操作中可能面临多重挑战:

  ·后端开发未完成: 后端服务可能尚在开发中,导致无法展开全面的测试。

  · 权限限制: 测试人员可能因为权限问题无法访问后端数据,从而限制了对数据交换过程的验证。

  · 第三方开发: 后端服务可能由第三方团队开发,增加了对其进行有效集成测试的难度。

  这些因素使得客户端的测试范围受到限制,集成测试变得愈加困难。尤其是在验证客户端数据发送的完整性和正确性时,团队面临着巨大的挑战。在这种复杂的系统架构中,确保客户端发送的数据能够准确地到达服务器至关重要。

  今天的示例中通过搭建一个WireMock 服务器模拟接受客户端发送的数据,然后在一定时间内去验证服务器接收到数据数量与次数,从而达到验证客户端是否发送了预期的数据。

  注意,这个用例与现实业务中的连接挑战有3个方面: 1) 依照场景测试要求自动化触发API 请求的功能点,2) 把现实业务中的请求地址改成WireMock 中配置的路径,3) 依照业务需求验证记录监控中数据与期待值的一致性。 为了解决这些挑战,有的需要和开发沟通,有的要和业务需求方进行讨论。唯有这样,才能更准确的进行自动化测试。

  温馨提示:如果您要按着示例一起做,请务必配置如下工具以及学习相关的知识。 用例主要是搭建启动一个WireMock 服务器,利用Rest-Assured完成API 的数据发送 (实际项目中,要通过客户端项目的功能触发API 的数据发送) ,然后通过验证服务器接收到的数据来确认API 的数据发送是否成功。

  · IDE: IntelliJ IDEA

  · 语言: Java

  · API 请求: Rest-Assured

  · API服务器 :WireMock

  · 测试框架:TestNg

  · 项目类型: Maven

  知识重点:

  · WireMock 的POST 构建 与启动 : 了解如何创建和配置 WireMock 服务器,设置 POST 请求的 stub,以便模拟真实的 API 行为。

  · Rest Assured 模拟API 请求:使用 Rest Assured 库发送模拟的 API POST 请求

  · WireMock API 获取POST 数据: 掌握如何通过 WireMock API 获取接收到的 POST 数据,以进行后续的验证和分析。

  一、 分解用例

  假设有一个待测试的系统功能,即数据发送功能,测试的重点是确保该功能发送的数据完整性。最好的方法是在触发数据发送后,前往服务器端将接收到的数据与发送的数据进行对比,以验证每个字段的正确性并确保没有数据丢失。同时,对于其他测试要求,比如连续触发该功能的数据发送,以确保该功能没有延迟或丢失任何数据。目前遇到的问题是,无法直接访问服务器端验证接收到的数据。

  对于这个测试需求,本示例使用 WireMock 工具搭建了一个模拟服务器,来替代真实的后端服务。通过这种方式,可以在本地测试数据发送功能:

  · 搭建并启动Mock 服务器 : 创建一个 WireMock 服务器来模拟真实的后端数据接收接口。

  · 发送数据: 触发本地系统的数据发送功能(示例中使用 RestAssured 模拟数据发送,为了更好地验证数据发送功能,这里数据的发送是在一个新线程中完成的。这样就能动态地监控服务器接收数据的状况,换句话说就能动态地测试当前系统的数据发送功能。)

  · 监控记录请求: Mock 服务器会记录所有接收到的请求,包括发送的数据。

  · 比对数据: 在监控过程中,可以WireMock 服务器里获取记录的数据从而来确定数据发送功能未被篡改且没有丢失数据。同时在验证大量数据发送过程中,可以通过设置超时,来确认数据发送功能没有延迟或数据丢失。

  理解了基本的测试场景,以及相对应的测试步骤流程。一起来实现它吧,让测试变得更加高效起来吧。

  二、 Maven 配置

  本次示例搭建 WireMork 服务器需要用到的3个插件。

   
   com.github.tomakehurst
   wiremock-jre8
   3.0.1
   pom
   test
   
   
   org.eclipse.jetty
   jetty-servlet
   11.0.22
   
   
   org.eclipse.jetty
   jetty-server
   11.0.22
   

  三 、代码解析

  以下代码给出了完整的测试用例执行流程,且对每一个方法进行了逐一解释。

  1.构建WireMock 服务器: 方法startWireMockServer() 先创建了 WireMockServer 实例, 并按接收到的指定端口进行监听。接着,启动服务器并配置 WireMock,使其能够处理特定的 HTTP 请求。最后,它设置一个 stub,以便在接收到 POST 请求到 /api/data/receive 的时候,返回一个 200 状态的响应,并包含 Content-Type 为 application/json 的头信息。

  2.新建一个新线程Thread dataSendRequest 执行sendPost()方法。sendPost 利用RestAssured 会每隔100 毫秒发送一次数据到MockServer,重复发送100 次且从第51次开始,发送内容testPostInvalid。

  3.dataValidation() 方法先通过调用receivedDataMonitor()方法获取MockServer 收到的请求数据接着,再对请求的数据进行内容与大小的验证。

  4.receivedDataMonitor()方法每500 毫秒循环一次地按接收到的时间分别把请求的次数保存以做延迟性验证(示例中只给了10秒,只是为了讲解用例而已)。

  5.最后是合并线程和关闭MockServer 服务器。

   @Test(description = "To Verify Data Sending Integrity ")
   public void testDataIntegrity() {
   // Setup Mock Server
   wireMockServer = startWireMockServer(9090);
   long threshold = 10;
   int eventSentCount = 100;
   //Step To Send Data In a New Thread
   baseURI = "http://localhost:9090/api/data/receive";
   Thread dataSendRequest = new Thread(() -> {
   try {
   sendPost(eventSentCount, 100);
   } catch (Exception e) {
   System.out.println("Error: Sending Data");
   }
   });
   dataSendRequest.start();
   // Verify Data Sending Integrity
   dataValidation(eventSentCount, threshold);
   // Send Data Thread Join
   try {
   dataSendRequest.join();
   } catch (InterruptedException e) {
   throw new RuntimeException(e);
   }
   // Stop WireMock Server
   stopWireMockServer(wireMockServer);
  }
  ——————————
   WireMockServer startWireMockServer(int port) {
   WireMockServer wireMockServer = new WireMockServer(port);
   wireMockServer.start();
   configureFor("localhost", port);
   stubFor(post(urlEqualTo("/api/data/receive"))
   .willReturn(aResponse()
   .withStatus(200).
   withHeader("Content-Type", "application/json")));
   return wireMockServer;
  }
  void sendPost(int repeatTime, int interval) {
   String requestBody = null;
   long startTime = System.currentTimeMillis();
  for (int i = 0; i < repeatTime; i++) {
   if (i < repeatTime / 2) {
   requestBody = "{\"testPost\":\"testing data\"}";
   } else {
   requestBody = "{\"testPostInvalid\":\"testing data\"}";
   }
   given().contentType("application/json").body(requestBody).post();
   // Wait for interval
   waitForNext(interval);
  }
  long endTime = System.currentTimeMillis();
  long totalTime = endTime - startTime;
  System.out.println("Total Time Spent: " + totalTime + " Milliseconds");
  }
  }
  ——————————
  void dataValidation(int expectedReceivedDataCount, long threshold) {
   ObjectMapper objectMapper = new ObjectMapper();
   List serverEvents = receivedDataMonitor(expectedReceivedDataCount, threshold);
   int totalContentReceived = 0;
   int invalidContentSize = 0;
   int invalidContent = 0;
   // Validate Post Count
   if (!(serverEvents.size() == expectedReceivedDataCount)) {
   System.out.println("Error Received Post Count Does Not As Expected");
   } else {
   // Validate Post Content
   for (ServeEvent event : serverEvents) {
   totalContentReceived++;
   String postBody = event.getRequest().getBodyAsString();
   try {
   Map postData = objectMapper.readValue(postBody, new TypeReference>() {
   });
   // Validate Size Of Each Post
   if (postBody.length() > 10240) { // 10 KB limit
   invalidContentSize++;
   //System.out.println("Error: Post Content Exceeds 10 KB: " + postBody);
   }
   // Validate Field Existence In Each Post
   if (!postData.containsKey("testPost")) {
   invalidContent++;
   //System.out.println("Error: Post Content Does Not Include 'testPost': " + postBody);
   }
   } catch (IOException e) {
   System.out.println("Error: Failed to Parse Post Body: " + postBody);
   }
   }
   }
   System.out.println("Info Total Received Post Count : " + totalContentReceived);
   System.out.println("Info Total Received Post Count With Invalid Content Size: " + invalidContentSize);
   System.out.println("Info Total Received Post Count With Invalid Content : " + invalidContent);
   Assert.assertEquals(expectedReceivedDataCount, serverEvents.size(), "Error Received Post Count Does Not As Expected");
   Assert.assertEquals(expectedReceivedDataCount, totalContentReceived, "Error Total Received Post Count As Expected");
   Assert.assertEquals(invalidContentSize, 0, "Error Total Received Post Count With Invalid Content Size Not As Expected");
   Assert.assertEquals(invalidContent, 50, "Error Total Received Post Count With Invalid Content s Not As Expected");
  }
  ——————————
  private List receivedDataMonitor(int expectedReceivedDataCount, long threshold) {
   long startTime = System.currentTimeMillis();
   long timeBox = TimeUnit.SECONDS.toMillis(40);
   long firstInterval = TimeUnit.SECONDS.toMillis(threshold);
   long loopStartTime = System.currentTimeMillis();
   long elapsedTime = 0;
   List serverEvents = null;
   boolean bValidation = false;
   int totalReceivedPCCount =0;
   while (System.currentTimeMillis() - startTime < timeBox) {
   serverEvents = wireMockServer.getAllServeEvents();
   long currentTime = System.currentTimeMillis();
   // Determine Posts Count Were Received in As Per Threshold
   if (currentTime - startTime < firstInterval) {
   receivedPCWithInThreshold = serverEvents.size();
   } else {
   receivedPCOutOfThreshold = serverEvents.size() - receivedPCWithInThreshold;
   }
   //System.out.println("Info Received Post Count (First " + threshold + " seconds): " + receivedPCWithInThreshold);
   //System.out.println("Info Received Post Count (After " + threshold + " seconds): " + receivedPCOutOfThreshold);
   totalReceivedPCCount = receivedPCWithInThreshold + receivedPCOutOfThreshold;
   if (totalReceivedPCCount == expectedReceivedDataCount && !bValidation) {
   long currentLoopTime = System.currentTimeMillis();
   elapsedTime = currentLoopTime - loopStartTime;
   System.out.println("Info Received Post Count " + receivedPCWithInThreshold + " Within " + threshold + "(Mills)");
   System.out.println("Info Received Post Count " + receivedPCOutOfThreshold + " Out of " + threshold + "(Mills)");
   System.out.println("Info Received Post Count " + totalReceivedPCCount + " Spend Total Period (Mills): " + elapsedTime);
   bValidation = true;
   }
   if (totalReceivedPCCount > expectedReceivedDataCount) {
   System.out.println("Error: Received Post Count Exceeds Expected " + expectedReceivedDataCount);
   }
   waitForNext(500);
   }
   boolean verifiedDataReceived = totalReceivedPCCount == expectedReceivedDataCount;
   Assert.assertTrue(verifiedDataReceived && elapsedTime<=15000,"Error: Received Post Count Exceeds Expected" );
   return serverEvents;
  }
  ——————————
  void stopWireMockServer(WireMockServer wireMockServer) {
   wireMockServer.stop();
  }

  四、结语

  如上所述,这种方法能够很快地确保验证客户端请求的次数与内容的准确性在限制的时间内,并根据业务需求进一步验证请求具体内容和大小。从而提升系统的可靠性和数据一致性。用在自动化测试框架里,既能保证快速有效的测试,同时也能保证测试结果的稳定性,从而降低代码的维护。此外,它还能够准确定位潜在问题,使开发团队更高效地进行调试和优化,从而进一步提升系统的整体质量。


文末了,我邀请你进入我们的软件测试学习交流群,大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,了解测试行业的最新趋势,助你快速进阶Python自动化测试/测试开发,稳住当前职位同时走向高薪之路。

最后:

1)关注+私信回复:“测试”,可以免费领取一份10G软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!

2)关注+私信回复:"入群" 就可以邀请你进入软件测试群学习交流~~

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1644.html

标签: objectmapper
分享给朋友:

“软件测试中数据验证的3大难题,这个工具搞定!” 的相关文章

vue3中父子传值、defineProps用法、defineEmits用法

Vue3中新增了一个 script setup 语法糖模式,可以在单文件组件中更简洁地编写组件逻辑。使用 script setup 语法后,props、data、computed、methods 等选项不再需要独立定义,而是可以直接在 setup 函数中声明,代码结构更加清晰,并且可以更方便地使用响...

java调用API操作GitLab

最近需要在一个WEB项目中集成GitLab,用到了GitLab的API操作,在网上找了很久都是说直接调用GitLab的Http接口,而且API官方只有javadoc没有其它说明文档,特别记录下,以备查询。这里采用Token的认证方式,因此需要先登陆GitLab新建一个Token,创建方式如下:创建完...

壹啦罐罐 Android 手机里的 Xposed 都装了啥

这是少数派推出的系列专题,叫做「我的手机里都装了啥」。这个系列将邀请到不同的玩家,从他们各自的角度介绍手机中最爱的或是日常使用最频繁的 App。文章将以「每周一篇」的频率更新,内容范围会包括 iOS、Android 在内的各种平台和 App。本期继续歪楼,由少数派撰稿作者@壹啦罐罐介绍他正在使用的...

Gemini应用在Android上广泛推出2.0闪电模式切换器

#头条精品计划# 快速导读谷歌(搜索)应用的测试频道在安卓设备的双子应用中推出了2.0闪电实验功能,现已向稳定用户开放。双子应用通过谷歌应用运行,目前推出的15.50版本中,用户可通过模型选择器体验不同选项,包括1.5专业版、1.5闪电版和2.0闪电实验版。2.0闪电实验模型提供了更快的响应速度和优...

js中数组filter方法的使用和实现

定义filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。语法var newArray = arr.filter(callback(element[, index[, selfArr]])[, thisArg])参数callback循环数组每个元素时调用的回调函数。回调函...

一套代码,多端运行——使用Vue3开发兼容多平台的小程序

介绍Vue3发布已经有一段时间了,从目前来看,其生态还算可以,也已经有了各种组件库给予了支持,但是不管是Vue3还是Vue2都无法直接用来开发小程序,因此国内一些技术团队针对Vue开发了一些多端兼容运行的开发框架,今天来体验一下使用Taro来体验一下使用Vue3开发多平台运行的小程序,以便于兼容各大...