基于 Junit 的接口自动化测试框架实现(1)

发表于:2021-10-28 09:51

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:佚名    来源:知乎

  分层的自动化测试
  5~10 年前,我们接触的自动化测试更关注的是 UI 层的自动化测试,Mercury 的 WinRunner/QTP 是那个时代商业性自动化测试产品的典型代表,在那个时代大家单纯想的都是能用一个自动化操作的工具替代人力的点击,商业化或是私有化框架大行其道。
  而分层的自动化测试倡导产品的不同阶段(层次)都需要自动化测试。在《Google 软件测试之道》中,在 Google 70% 的投入为单元测试(小型测试),20% 为接口 / 集成测试(中型测试),10% 为 UI 层的自动化测试(大型测试),也就是大家熟悉的金字塔模型,越往上自动化实现难度越大,投入产生的收益也越低(需要强调的是,UI 层的自动化测试作为最接近用户操作的测试,仍然有其存在的意义和场景)。
  接口测试的意义
  接口测试是验证两个或多个模块应用之间的交互(通常是采用接口的方式),测试的重点是要检查数据的交换,传递和控制管理过程,还包括处理的次数。
  接口测试的核心战略在于:以保证系统的正确和稳定为核心,以持续集成为手段,提高测试效率,提升用户体验,降低产品研发成本。
  接口测试要为代码的编写保驾护航,增强开发人员和测试人员的自信,让隐含的 BUG 提前暴露出来,要让开发人员在第一时间修复 BUG,要让业务测试人员在测试的时候更加顺手,最大限度得减少底层 BUG 的出现数量,要让产品研发的流程更加敏捷,要缩短产品的研发周期,最后在产品上线以后,要让用户用得更加顺畅,要让用户感觉产品服务零缺陷。
  不同于单元测试,接口测试本质上还是一种黑盒的测试,所以非常适合专职测试工程师去参与和覆盖。
  接口测试框架选型
  目前接口测试框架的选型,最常见的方法是采用 JMeter、soapUI、postman、robotframework 等 UI 化的接口测试框架来做。
  好处是业务测试人员可以不用或很少写测试代码,入门门槛低,前几年有很多公司都曾经开发过类似的测试框架,有前端有后端,专职的测试开发人员维护,业务测试人员只需要知道怎么操作而不需要参与具体 coding。
  这种方法看起来非常高大上,但实际的问题是执行过程中主要的工作变成了测试框架的维护,非常依赖专职测试开发人员的设计和开发能力,每增加一种新的接口协议(比如 Dubbo、Hessian 或者内部自定义的协议)就需要在框架上增加支持;更致命的是一旦核心测试开发人员出现流动,就很容易造成整个接口测试体系的崩塌;另外对业务测试人员的技能成长也并不公平,个人已面试过太多只会使用某大公司 XXX 测试框架却完全不了解具体实现方式的工程师。
  《Google 软件测试之道》中早已有过预言,保密和私有化的基础测试设施并不能获得想象中的好处,这种方式意味着昂贵和迟缓,即使在公司内部的不同项目之间也很难做到复用。未来的测试基础设施必然是建立在共享代码和开源框架的基础上,测试开发人员需要更多的利用开源项目并为之贡献。最近重读了一次这本几乎改变软件测试行业的书籍,发现里面的预言都是如此准确,当然也可以认为国内整个行业都正参照 Google 的方式在进行演变。
  使用 Junit、Testng 等 Java 接口框架,直接编写测试代码去测试,同时对一些重复性的工作抽象建立基础库或方法。
  有点类似于单元测试,这种方法扩展性好实现灵活,作为程序员可以用代码实现灵活的场景组织和功能,只要稍微二次开发一下,但需要测试工程师有一定的编码基础。 
  这种方式在前几年实施的难度还是比较大,因为在市场上要找到懂 Java 代码的测试工程师都寥寥无几,但在对测试工程师开发能力要求越来越多的今天实施难度已没有想象中困难,Java/Python 等语言的编码能力也已成为我们团队招聘时的基本要求。
  另外提下,这里使用 Java 而不用其他语言的原因,主要是团队的技术储备 Java 是强项 , 拥有丰富的开源测试库,而且一般互联网公司的产品基本都是采用 Java 框架进行开发,和开发团队技术栈保持一致非常有必要性。
  GTest 接口测试框架
  有很多公司做了各种不同的接口框架,都是基于自己公司的业务基础设计开发。我们基于自己的业务特点也封装了自己的接口测试框架 gtest-framework,在开发人员的单元测试中也正逐渐使用。
  gtest-framework 要做的事情:
  1)前置数据准备和自动清理。
  2)常见接口协议的实现和封装。
  3)依赖注入配置方式的支持。
  4)如文件、图片、xml、字符等各类通用处理方法的集成。
  5)断言方式的扩展等。
  接口测试关键实践
  1. 数据准备
  接口测试的数据准备,一般是指数据库的数据准备,有时候还包括文件和缓存的数据准备。具体实现可以从下面几个方面去考虑:
  (1)硬编码的方式准备测试数据,在写测试代码的时候,使用到什么数据就插入什么数据。为了避免数据重复,很多人会习惯于使用随机字符或随机数(这种方法可能造成测试用例不稳定,尽量避免)。
  (2)可以直接通过调用其他 API 的方式准备测试数据,这种情况在测试最上层服务的时候比较有用,比如测试购买商品,就需要准备要购买的商品数据,购买商品的用户数据,这个时候,可以直接调用生成商品的 API 和生成用户的 API 直接生成测试数据。此方法实现简单,但前提是需要具备相应的 API 并且此 API 功能正确。
  (3)使用 Excel 或 XML 准备测试数据,这种准备测试数据的方式,主要针对对象数据的准备,比如可以将一条商品数据对应 Excel 中的一条数据,因为一般开发都会使用 pojo 映射,而在准备测试数据的时候,这些 pojo 对象属性的设置往往是重复和大工作量的,用 Excel 或 XML 方式准备,则可以减少在代码当中重复去准备这些数据。
  一般我们使用的是 2/3 两种方式,其中 3 这种方式主要利用 Dbunit、Spring-test、Unitils 等测试框架的特性经二次开发增加自定义注解,很轻松的导入 Excel 或 XML 格式的文件并在测试完成后对数据进行自动回滚。
  /** 
  * @ClassName: TestJdbcDataSet 
  * @Description: 采用自定义 TestDataSet 注解方式准备测试数据,推荐。
  * @author Cay.Jiang   
  * @date 2017 年 7 月 10 日 上午 9:10:29 
  *  
  */
  public class TestJdbcDataSet extends BaseCase{
      Map<String, Object> args = new HashMap<String, Object>();
      @Test
      @TestDataSet(locations={"/tmp/domaininfo.xls"},dsNames={"mysqlDataSource"})
      public void test01_mysql(){
          args.put("selfdomain", "baidupc2");
          List<Map<String, Object>> result=JdbcUtil.queryData(mysqlJdbcTemplate, "domaininfo", args);
          System.out.println(result);
          assertEquals(" 合作商接入名称 ",result.get(0).get("remark"));
      }
  }
  上面代码中的 /tmp/domaininfo.xls 参见:domaininfo.xls ,其中 Excel 格式以 Sheet 名为表名,第一行定义了字段名称,其余行为对应的数据。
  多数据集:
  @TestDataDataSet(locations={"Data1.xls","Data2.xls"},dsNames={"dsNameA","dsNameB"}),Data1.xls 的数据会插入 dsNameA 所指的数据库中,Data2.xls 的数据会插入 dsNameB 所指的数据库中。
  2. 断言
  常见的断言方式有 JUnit 自带的 Assert 和 Hamcrest。JUnit 自带的断言方法功能十分有限只能满足最基本的需求。Hamcrest 相对来讲功能丰富一些,但是该库已经多年不更新。而且 Hamcrest 和 JUnit 自带的断言方法一样,有个致命的缺点,就是当一个 case 中有多个断言时,如果其中一个断言失败,那么在它之后的断言都不会执行。这里向大家推荐一款新的断言神器 AssertJ。
  AseertJ: 号称流式断言。什么是流式,常见的断言器一条断言语句只能对实际值断言一个校验点,而 AseertJ 支持一条断言语句对实际值同时断言多个校验点,这样使得断言的语句更加简洁适合阅读。AseertJ 还支持一次性执行所有断言,然后收集所有失败的断言一起反馈。当然除此之外 AseertJ 还有很多其他特性,可以参考官方文档慢慢挖掘。下面将举例说明一下 AseertJ 的优势:
  public class TestCase extends BaseCase{
      UserProfileBO user = new UserProfileBO();
      @Before
      public void init(){
          user.setAddress(" 杭州 ");
          user.setMobile("1386800000");
          user.setUserName(" 测试账号 ");
      }
      /*
       * JUnit 内置的断言
       * 
       * 1、其中一个断言失败后,后面所有断言将不会执行。
       * 2、支持的断言方法较少
       * 
       */
      @Test
      public void testAssertJUnit(){
          Assert.assertEquals(" 地址 ",user.getAddress()," 宁波 ");
          Assert.assertEquals(" 手机 ",user.getMobile(),"13868000000");
          Assert.assertEquals(" 手机 ",user.getUserName()," 测试 ");
          Assert.assertNotNull(user.getMobile());
          Assert.assertTrue(user.getMobile().startsWith("138"));
          Assert.assertTrue(user.getMobile().length() == 11);
      }
      /*
       * Hamcres 断言
       * 
       * 1、其中一个断言失败后,后面所有断言将不会执行。
       * 2、支持的断言方法丰富,但是已经多年不更新。
       * 
       */
      @Test 
      public void testHamcrestMatchers() {  
          MatcherAssert.assertThat(user.getAddress(), equalTo(" 宁波 "));  
          MatcherAssert.assertThat(user.getMobile(), equalTo("13868000000"));  
          MatcherAssert.assertThat(user.getUserName(), equalTo(" 测试 "));  
          MatcherAssert.assertThat(user.getMobile(), allOf(is(nullValue()),startsWith("136")));  
      }
      /*
       * AssertJ 断言
       * 
       * 1、支持所有断言执行后,失败断言统一反馈。
       * 2、支持的断言方法丰富。
       * 3、支持流式断言,方便阅读。
       * 
       */
      @Test
      public void testAssertJ(){
          // 断言集合,执行所有断言后,失败断言统一反馈。
          SoftAssertions.assertSoftly(softly -> {
              softly.assertThat(user.getAddress().equals(" 宁波 "));
              softly.assertThat(user.getMobile().equals("13868000000"));
              softly.assertThat(user.getUserName().equals(" 测试 "));
          });
          // 流式断言
          Assertions.assertThat(user.getMobile())
              .isNotNull()
              .startsWith("136")
              .hasSize(11);
      }
  }

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号