单元测试③| 单元测试实践——构建优秀

3. 单元测试实践——构建优秀

3.1 在组织中引入单元测试

在一个组织中成功的引入测试驱动开发和单元测试并集成到该组织的文化中,对该组织的发展和团队的效率将会有极大的提升。然后有时候这个引入会失败,成功的组织则存在一些共性的东西,我们在这一节将探讨一下如何增加引入单元测试的成功率。

在任何类型的组织中,改变人们的习惯多半与心理学有关,而并非是技术问题。人们不喜欢变化,而且变化常常伴随着很多的FUD(fear, uncertainty, and doubt——害怕、不确定性和怀疑)。于是如何说服组织成员或者让组织接受新的变化,并不是一件容易和轻松的事情。

  • 怎样成为变革推动者

    开始实施变革之前,人们会开始对它们关心的事情提出棘手的问题,例如这样做会“浪费”多少时间?这对于开发人员来说意味着什么?我们怎么知道它有效呢?这些问题可以尝试用下面的成功的方式进行解决。你会发现,当你能够回答这些问题,并说服组织中的其它人,会对组织的变革提供非常大的帮助。

    这里有一些帮助的小提示:

    • 选择较小的团队
    • 新建子团队
    • 考虑项目的可行性

    此外,在变革中需要找到阻碍者,并寻找到它们不愿意进行单元测试的尝试的原因所在,加以解决。此外可以考虑赋予它们全新的职责,会让它们觉得被依赖而且对组织有意义。

  • 成功之路

    组织或者团队开始改变流程主要有两个方式:自下而上或者自上而下。

    1. 自下而上:先说服程序员,使得程序员采纳并且提倡,然后产生组织的变革,最终说服管理层接受。
    2. 自上而下:经理通过给团队做一个演示来开始实施,或者使用自己的权力进行推动变革。

    代码的完整性,Code Integrity

    这个术语,通常意味着代码做它该做的事,而团队知道代码不能做哪些事。

    代码的完整性包括如下实践:

    • 自动化构建
    • 持续集成
    • 单元测试与测试驱动开发
    • 代码一致性和商定的质量标准
    • 尽量快速的修复缺陷

    为了“我们的代码完整性很好”这个目标,也可以开始如上的实践。

  • 锁定目标

    没有目标,将会很难衡量改变,并且与他人交流。可以考虑下面的目标

    1. 提高代码测试覆盖率
    2. 提高相对代码改动量的测试覆盖率
    3. 减少重复出现的缺陷
    4. 减少修复缺陷的平均时间

3.2 使用 Maven 运行 JUnit 单元测试

  • Maven的功能与安装

    Maven 是一个用于项目构建的工具,通过它便捷的管理项目的生命周期。同时 Maven 不只是一个简单的项目构建工具,还是一个依赖管理工具和项目信息管理工具。它提供了中央仓库,能帮我们自动下载构建。

    在之前的课程中,我们使用 IDEA 工具,通过直接导入 JUnit 的*.jar包文件,进行单元测试的构建的。在这里我们继续使用 Maven 作为构建工具,来构建 JUnit 单元测试项目。

    首先,不要相信任何 IDE(Integration Development Environment,集成开发工具)中自带的 Maven 插件,包括 IDEA自带的。那么我们需要安装 Maven。

    具体的安装步骤如下:

    • 检查 JDK 的安装

      在安装 Maven 之前,首先要确认已经正确的安装了 JDK。Maven 可以运行在 JDK 1.4 以及以上的版本。目前的 JDK 1.8 的版本是可以的。需要下载 JDK 并进行安装。安装好 JDK 以后,需要检查 %JAVA_HOME% 的环境变量是否设定。

      输入 cmd | 打开 Windows 命令行, 输入 echo %JAVA_HOME%

      Microsoft Windows [Version 10.0.14393]
      (c) 2016 Microsoft Corporation. All rights reserved.
      
      C:\Users\xxx>echo %JAVA_HOME%
      C:\Program Files\Java\jdk1.8.0_66
      

    • 下载并安装 Maven

      Maven 可以免费在官网下载并安装。打开 Manve 的下载页面,下载针对所用平台的对应的版本,然后在 C 盘解压即可。

      Maven 的下载地址:https://maven.apache.org/download.cgi

      解压以后,需要设定 Windows 的环境变量。

      1. %M2_HOME%:在系统变量添加,路径为安装的 Maven 的根目录,例如 C:\Apache\apache-maven-3.3.9
      2. path:在系统变量中,找到path,添加上去 ;%M2_HOME%\bin;
      3. 重新打开 Windows 命令行,输入 mvn -version
      Microsoft Windows [Version 10.0.14393]
      (c) 2016 Microsoft Corporation. All rights reserved.
      
      C:\Users\xxx>mvn -version
      Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
      Maven home: C:\Apache\apache-maven-3.3.9
      Java version: 1.8.0_66, vendor: Oracle Corporation
      Java home: C:\Program Files\Java\jdk1.8.0_66\jre
      Default locale: en_US, platform encoding: GBK
      OS name: "windows 10", version: "10.0", arch: "amd64", family: "dos"
      

  • 建立一个Maven项目

    使用 IDEA 新建 Maven Project,并添加依赖如下:

     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
       <scope>test</scope>
     </dependency>
    

    在弹出的浮层中点击“Enable Auto-import”即可。

    然后在 src/main/test/java 文件夹下面可以新建 Java Class 进行测试类的编写。

    将被测试的类 放在 src/main/java 的文件夹下。

  • 使用Maven生成JUnit报告

    Maven 本身并不是一个单元测试框架,能做的只是在构建执行到特定生命周期阶段的时间,通过插件来执行 JUnit 的测试用例。这个插件就是 maven-surefire-plugin,可以称之为测试运行器。

    默认情况下,maven-surefire-plugin 的 test 目标会自动执行测试用例源码路径(默认为 src/main/test/java/)下所有符合一组命名模式的测试类。这组模式为:

    模式 描述
    **/Test*.java 任何子目录下所有命名以 Test 开头的 Java 类
    **/*Test.java 任何子目录下所有命名以 Test 结尾的 Java 类
    **/*TestCase.java 任何子目录下所有命名以 TestCase 结尾的 Java 类

    按照上述描述的模式,添加以下依赖:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.19.1</version>
      <configuration>
        <skipTests>false</skipTests>
        <source>1.8</source>
        <target>1.8</target>
        <includes>
          <include>**/*Tests.java</include>
          <include>**/*TestCase.java</include>
        </includes>
      </configuration>
    </plugin>
    

    然后在需要运行的目录中,执行mvn test,便可完成测试,并生成报告。默认情况下,maven-surefire-plugin 会在项目的 target/surefire-reports 目录下生成两种格式的错误报告:

    • 简单文本格式
    • 与 JUnit 兼容的 XML 格式

    这样的报告对于获得信息足够了,XML 格式的测试报告主要是为了支持工具的解析,是 Java 单元测试报告的事实标准。

3.3 单元测试框架在自动化测试中的应用

  • 自动化测试的介绍

    当前,软件测试贯穿到整个软件开发生命周期的全过程中,不再停留在编程之后的某个阶段,尤其是敏捷开发开始广泛的应用于互联网行业以后,敏捷测试就把软件测试解释为对软件产品质量的持续评估。在敏捷方法中,持续测试被提倡。当前的持续测试的实施,主要依托于持续集成。

    自动化测试:以人为驱动的测试行为转化为机器执行的一种过程

    这里我们使用 Selenium 工具进行自动化测试的应用。

    Selenium is a suite oftools to automate web browsers across many platforms.

    selenium 硒,/sə'liniəm/

    Selenium是开源的自动化测试工具,它主要是用于Web 应用程序的自动化测试,不只局限于此,同时支持所有基于web 的管理任务自动化。

    Selenium 是用于测试 Web 应用程序用户界面 (UI) 的常用框架。它是一款用于运行端到端功能测试的超强工具。您可以使用多个编程语言编写测试,并且 Selenium 能够在一个或多个浏览器中执行这些测试。

  • 使用 JUnit + Selenium 进行自动化测试

    接下来我们使用 Junit + Selenium 构建自动化测试

    步骤如下:

    • 安装 Java 和 IDEA

    • 使用 IDEA 创建 Maven Project,并使用如下 pom.xml 文件

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://maven.apache.org/POM/4.0.0"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <parent>
              <groupId>org.seleniumhq.selenium</groupId>
              <artifactId>selenium-parent</artifactId>
              <version>2.53.1</version>
          </parent>
          <artifactId>selenium-server</artifactId>
          <name>selenium-server</name>
          <dependencies>
              <dependency>
                  <groupId>org.seleniumhq.selenium</groupId>
                  <artifactId>selenium-java</artifactId>
                  <version>${project.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.seleniumhq.selenium</groupId>
                  <artifactId>selenium-remote-driver</artifactId>
                  <version>${project.version}</version>
              </dependency>
              <dependency>
                  <groupId>commons-io</groupId>
                  <artifactId>commons-io</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-exec</artifactId>
              </dependency>
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
              </dependency>
              <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-csv -->
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-csv</artifactId>
                  <version>1.4</version>
              </dependency>
              <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>6.0.3</version>
              </dependency>
          </dependencies>
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-surefire-plugin</artifactId>
                      <version>2.19.1</version>
                      <configuration>
                          <skipTests>false</skipTests>
                          <includes>
                              <include>**/*Tests.java</include>
                          </includes>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>
      

    • 在 src/test/java 下创建 Java Class 进行编写自动化测试脚本。脚本如下:

      public class RanzhiTestCase{
          // 声明成员变量
          private WebDriver driver;
          private String baseUrl;
      
          @Before
          public void setUp(){
              this.driver = new FirefoxDriver();
              this.baseUrl = "http://localhost:808/ranzhi/www";
          
          @After
          public void tearDown(){
              this.driver.quit();
          }
      
          @Test
          public void testLogIn() {
              // 声明局部变量,传递全局的driver给它进行操作
              WebDriver driver = this.driver;
              // 步骤1
              // 用局部变量driver 打开然之的登录地址
              driver.get(baseUrl);
              // 让java代码停止运行1秒钟,等待浏览器进一步响应
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      
              // 断言:检查是否打开了正确的登录地址
              Assert.assertEquals("登录页面打开错误",
                      this.baseUrl + "/sys/user-login-L3JhbnpoaS93d3cvc3lzLw==.html",
                      driver.getCurrentUrl());
              // 步骤2
              // 输入用户名 密码 进行登录
              // 输入用户名
              WebElement accountElement = driver.findElement(By.id("account"));
              accountElement.clear();
              accountElement.sendKeys("admin");
              // 输入密码
              WebElement passwordElement = driver.findElement(By.id("password"));
              passwordElement.clear();
              passwordElement.sendKeys("123456");
              // 点击登录按钮
              driver.findElement(By.id("submit")).click();
      
              try {
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              Assert.assertEquals("登录页面登录跳转失败",
                      this.baseUrl + "/sys/index.html",
                      driver.getCurrentUrl());
          }
          }
      
    • Selenium 推荐的 Page Object 设计模式进行方案设计

      自动化测试方案(高阶版).png

3.4 使用 Jenkins 进行持续质量审查

  • 什么持续集成

    持续集成,Continuous integration ,简称CI。

    随着软件开发复杂度的不断提高,团队开发成员间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。尤其是近些年来,敏捷(Agile) 在软件工程领域越来越红火,如何能再不断变化的需求中快速适应和保证软件的质量也显得尤其的重要。

    持续集成正是针对这一类问题的一种软件开发实践。首先我们看一下,敏捷教父 Martin Fowler 对持续集成的定义:

    Martin Fowler:*Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly. *

    具体定义:持续集成式一种软件开发实践。它倡导团队的成员必须经常的集成它们的工作,通常至少每天一次甚至更多次集成。每次集成都需要通过自动化的构建(包括编译代码、构建应用、部署程序以及自动化测试)来验证,从而尽早尽快的发现集成中的错误。大量的团队利用这样的方式来更快的开发内聚的软件。大大减少此过程中的集成问题。

    具体的流程图如下:


    img

    持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元、自动化)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。

    首先,解释下集成。我们所有项目的代码都是托管在SVN服务器上。每个项目都要有若干个单元测试,并有一个所谓集成测试。所谓集成测试就是把所有的单元测试跑一遍以及其它一些能自动完成的测试。只有在本地电脑上通过了集成测试的代码才能上传到SVN服务器上,保证上传的代码没有问题。所以,集成指集成测试。

    再说持续。不言而喻,就是指长期的对项目代码进行集成测试。既然是长期,那肯定是自动执行的,否则,人工执行则没有保证,而且耗人力。对此,我们有一台服务器,它会定期的从SVN中检出代码,并编译,然后跑集成测试。每次集成测试结果都会记录在案。完成这方面工作的就是下面要介绍的Jenkins软件。当然,它的功能远不止这些。在我们的项目中,执行这个工作的周期是1天。也就是,服务器每1天都会准时地对SVN服务器上的最新代码自动进行一次集成测试。


    img
  • Jenkins环境搭建

    Jenkins,原名Hudson,2011年改为现在的名字,它 是一个开源的实现持续集成的软件工具。官方网站:http://jenkins-ci.org/

    Hudson是在2004年的夏天由Sun公司开发的(就是开发Java的那家),2005年2月开源并发布了第一个版本。Hudson发布的时候CruiseControl是CI界的老大哥,但是很快,在大约2007年的时候Hudson已经超越CruiseControl。2008年5月的JavaOne大会上,Hudson获得了开发解决方案类的Duke's Choice奖项。从此,小弟翻身做大哥,Hudson成为CI的代名词。

    2009年6月,Oracle收购Sun。2010年9月,Oracle注册了Hudson的商标,然后就没有然后了。原Hudson的成员创建了Jenkins。

    Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和提醒功能,还能用图表的形式形象地展示项目构建的趋势和稳定性。

    需要从官网下载Jenkins的文件,在本地安装Java(jdk)的环境以后,直接执行以下语句进行安装:

    java -jar jenkins.war
    

    随后访问http://localhost:8080即可

    最后,将Jenkins安装成Windows服务启动。

    在Jenkins的主页中选择 Manager Jenkins

    [图片上传失败...(image-b12cb3-1516466328793)]

    接下来选择 Install as Windows Service

    [图片上传失败...(image-9e447f-1516466328793)]

    在Installation Directory中选择jenkin的安装路径。这里会默认产生,直接点击Install就可以了。

    随后我们点击Yes,然后可以看到Windows服务中添加了Jenkins的服务,并已经设置为自动和启动状态。

  • 部署Jenkins与运行

    • 创建Jenkins Job

      Jenkins提供了四种类型的Job:

      1. 构建一个自由风格的软件项目:这个是Jenkins的主要功能,可以构建一个你自己需要的项目。
      2. 构建一个maven项目:这是基于maven构建的项目,整个过程将会基于你的.pom文件进行构建,大大减轻构建的配置
      3. 构建一个多配置项目:这种项目适用多配置的项目,比如多个平台定制的构建,多个测试环境的部署构建等。
      4. 监控一个外部的任务:这种事允许你记录和执行不在Jenkins中的Job,这些Job可以运行在远程的主机上,Jenkins通过远程自动构建,作为一个控制面板而存在。
    • 运行Jenkins Job

      运行Job只需要在页面的左侧选中已经列出的项目,进行操作就可以了。选择立即构建,便可以进行自动构建的工作了。

    • 定时构建 Job

      目前有一个每日构建的概念。

      Daily Build,每日构建。需要Jenkins在每日固定的时间进行代码自动构建、集成和测试的工作。那么需要定制执行时间。Jenkins的自动构建定制时间是遵循cron语法的。具体来说,每一行包括了5个用白空格或者Tab隔开的字段,分别是:MINUTE HOUR DOM MONTH DOW。具体的格式我们参考下图

      字段 说明 示例
      MINUTE Minutes within the hour (0–59) 30
      HOUR The hour of the day (0–23) 17
      DOM The day of the month (1–31) 1
      MONTH The month (1–12) 5
      DOW The day of the week (0–7) where 0 and 7 are Sunday. 5

      然后每个格式,都可以由*-/,4种字符组成:

      • *代表所有可能的值
      • -代表整数之间的范围
      • /代表指定的时间的间隔频率
      • ,代表指定的列表范围

      命令的格式参考和示例: 注意时间是伦敦时间

      分钟 小时 月份 星期 命令格式 描述
      H 16 1,10,20 * * H 16 1,10,20 * * 每个月的1,10,20日的16:00执行
      H 16 * * 1-5 H 16 * * 1-5 每个周的周一到周五的16:00执行
      30 17 * * 1,5 30 17 * * 1,5 每个周的周一和周五的17:30执行
    • Jenkins Job 示例

      ## 1. 创建SVN仓库 
      ## https://172.31.95.168/svn/DemoRepo/
      ## ciuser / ciuser
      ## 2. 从SVN仓库签出文件到CI Server
      ## 3. 构建 build
      ## 4. 部署 deploy
      ##  4.1 备份目标文件夹 
      ##  若有ranzhi_bak 需要先删除
          rd /s /q c:\xampp\htdocs\ranzhi_bak
      ##  把 htdoc的ranzhi改成 ranzhi_bak
          xcopy c:\xampp\htdocs\ranzhi c:\xampp\htdocs\ranzhi_bak\ /s /e /y
          rd /s /q c:\xampp\htdocs\ranzhi
      ##  4.2 复制构建的版本到目标文件夹
      ##  把workspace的 ranzhi 复制到 htdocs下面
          xcopy "%WORKSPACE%\ranzhi" c:\xampp\htdocs\ranzhi\ /s /e /y
      ##  4.3 恢复配置文件
      ##  复制 ranzhi_bak\config\my.php 到 ranzhi\config\my.php
          xcopy c:\xampp\htdocs\ranzhi_bak\config\my.php c:\xampp\htdocs\ranzhi\config\ /e
      ## 5. 自动化测试
      python D:\Git\Coding\BWFTraining\3.03_Selenium\codes\weekend2demo\ranzhi_test_runner.py
      

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容