[篇幅较长,10.15前补充完毕,如希望探索可直接移步Github仓库:https://github.com/SivilTaram/CITest]

在编程课中,我们可以使用成熟的在线评测系统来测试某个代码块或文件在功能实现上的正确性。但在软件工程课中,对项目的自动测试仍然是一个有挑战性的问题。一个比较复杂的软件工程项目往往由多个文件组成,开发者可能会调用不同的第三方库函数,使用不同的编译环境(比如Mac/Linux/Windows),这些因素导致了自动测试项目的复杂性。

早在布置数独项目给福州大学的学生时,我就意识到了自动测试项目不是一个可以一锤子做完的买卖。核心测试的代码并不难写,例如数独生成的项目,核心就是检查被测试程序生成的数独棋盘是否符合规范。按照我的经验,真正阻碍许多助教独立开发自动测试项目的因素主要是三点:

  1. 助教需要写一个爬虫自动爬取所有学生的Github仓库地址,将它们克隆到本机,并把每个项目仓库存储目录都重命名为学生学号以记录测试成绩。这一点要求助教能分析一些基本的网络请求,并利用代码构造请求。
  2. 助教要在布置项目时对学生的开发进行一些约束,并写一个带异常处理的测试程序来处理不同约束被破坏的情况。这就意味着,自动测试项目除了能够评测正确情况外,还要对一些约束不满足的情况给出合理的报错,比如说规定的 src 文件夹不存在。这一点要求助教完成一个鲁邦、友好又准确无误的测试程序,有些时候测试程序甚至比项目本身更复杂。
  3. 如果在布置项目时要求学生提交源码的同时也提交可执行程序(比如.exe),助教无法从源头上保证学生提交的可执行程序编译于他自己的代码;但如果不要求学生提交可执行程序,即使约束了指定编译环境与测试命令,助教也会面临大部分项目编译失败的风险(这是由项目复杂度引起的)。这一点要求助教在本机安装必要的编译环境,每个助教都需要做一遍环境配置,很慢很麻烦。

其实这三点问题多多少少都与测试策略的选择有关:目前软件工程课程中普遍采用后处理的自动测试策略,即学生完成开发后助教才进行测试,再过一定时间学生才能知道自己项目的错误。 但在实践中,大部分学生是无法一次通过测试,不能通过的原因往往不是代码没写好,而是因为他们遗漏或错误解读了作业细节。这种源于双方认知差异而导致的问题也不一定就是学生们的失误,也可能是老师或助教没有明确指定一些看似显然的约束条件。为了避免这种问题的产生,现有的测试流程中需要加入持续测试反馈的环节。结合前人的经验,本文制定了一套新的测试流程,其中的关键就在于 Git 提供的 Pull Request 与 Continuous Integration (持续集成,下面简称为CI) 工具。本文下面将分三个部分来说明新的测试流程,首先是前后测试流程的对比,接着是一个基于Travis CI与Github搭建的持续测试的最小工作示例,最后是教各位助教定制化自己的测试脚本。

测试流程

在传统软件工程课程的测试中,测试流程如下图所示:

  1. 收集地址: 每个学生建一个独立的仓库,将仓库地址评论到教师博客下(或者以其他方式收集)。
  2. 克隆仓库: 在作业截止日期后,助教克隆所有项目。
  3. 自动编译[可选]: 助教运行自动编译程序,得到能够编译项目的可执行程序。
  4. 自动测试: 助教运行自动测试程序,记录学生项目的错误日志。
  5. 错误反馈: 助教向学生反馈项目中出现的错误,学生进行更正。返回第 2 步。

在将持续测试反馈加入之前的测试闭环中后,新的测试流程如下图所示:

  1. 发布仓库: 教师发布一个公开仓库,学生 Fork。
  2. 静态检查: 学生完成开发,发起 Pull Request。CI 检测到新的 Pull Request 发起,运行教师预先设定的静态检查程序。
  3. 持续反馈: 完成静态检查后, CI 自动在相应的 Pull Request 中追加评测信息(包括是否通过初步检查,错误日志等)。学生看到评测信息后更正程序,回到第 2 步。
  4. 持续编译[可选]: 完成静态检查后,CI 运行教师预先设定的编译脚本,把由学生仓库编译得到的可执行程序补充 commit 到相应的文件夹下。
  5. 动态测试: 在作业截止日期后,助教运行动态测试程序,测试可执行程序在相应输入下是否可以得到期望输出,得到不同测试样例下的得分。

新的测试流程对助教显得更友好一些:Pull Request 的仓库收集方式避免了爬虫的工作量,CI 的自动反馈帮助学生持续地获得反馈,也使得助教不再需要定时反馈项目错误信息。同时,这种测试流程可以保证 git 仓库中的可执行文件一定是从学生自己的源代码编译得到的,不需要助教在本机安装任何编译器。

最小工作范例

接下来本文将展示一个基于上述测试流程的最小工作范例,主要用到的相关知识如下:

前两项是必须学会的背景知识,如果不会的话请先熟悉Git的基本命令与至少一门编程语言。

发布仓库

https://github.com/join 这个网址处申请注册一个Github账号,申请成功后可在https://github.com/login 处利用刚刚注册的账号进行登录,开始在Github上进行开发。

点击右上角的 + 号,选择 New repository

给 Respository 起个名字,比如叫 CITest,点击 Create repository 即可发布一个仓库。

持续集成工具对 Public 仓库是免费的,仓库默认Public;如果选择 Private 的话,持续集成工具会收费。

后续的 Fork 与发起 Pull Request 的步骤可以在这个教程中找到,这里不再赘述。

静态检查

静态检查,顾名思义,主要用于规范学生们提交仓库的组织目录与文件结构。例如检查仓库中是否有 src 文件夹是否有 main.java等,是否可以成功编译等。静态检查原则上不会运行仓库中的可执行程序。比如下面就是一个用Python写的静态检查样例程序:

import os
import sys def check_structure(dir_path):
# check if exist `src`
src_full = os.path.join(dir_path, "src")
if not os.path.exists(src_full):
raise AssertionError("没有 src 文件夹!") # check if exist `bin`
bin_full = os.path.join(dir_path, "bin")
if not os.path.exists(bin_full):
raise AssertionError("没有 bin 文件夹!") # check is exist `main.py`
main_full = os.path.join(dir_path, "src", "main.py")
if not os.path.exists(main_full):
raise AssertionError("在 src 文件夹下没有 main.py 文件!") if __name__ == '__main__':
check_dir = sys.argv[1]
try:
check_structure(check_dir)
sys.exit(0)
except AssertionError as e:
sys.exit(-1)

上面这个样例程序接收来自系统的参数check_dir,检查该目录下是否有以下文件夹或文件 src,bin,src/main.py。如果这些检查都通过了,就正常返回 0,否则返回错误码 -1。

因为静态检查是一种规范组织目录与编译环境的手段,并不涉及实际的测试样例。所以助教不必担心它被学生们看到,学生甚至也可以向静态检查贡献更丰富的错误类型。助教也不必追求一开始就完美,以学生作为直接反馈源迭代改进静态检查程序即可。

这样,我们就得到了一个非常简单的测试程序,把它命名为文件check.py,提交到刚刚建好的仓库中。

持续反馈

接下来就需要 CI 出场了,这里我们采用的是 Travis CI。首先,我们要激活 Github 仓库对应的 CI 工具。进入网站 https://travis-ci.org/,点击右上角的Sign in with Github,可以使用 Github 账号直接登陆到 Travis CI。

登陆成功后,点击左侧My Repositories 旁边的+,寻找要激活的 Github 仓库。

如果没有发现新建的CITest项目,可以点击左侧的sync account同步仓库。

找到仓库后,点击仓库链接,最后点击底部的Activate即可激活仓库的持续集成。

Travis CI 的基本原理是:用户依照规定的语法在想要持续集成的Github 仓库创建名为.travis.yml的配置文件。当新的Pull Request 发起时,Travis CI 自动克隆仓库,并提供一个Linux (或其他)虚拟环境运行.travis.yml中预设好的脚本,完成用户的预期功能。在有了check.py简易测试文件后,我们现在需要一个.tavis.yml配置文件来帮助它搭建运行环境。下面是一个非常简单的配置文件:

sudo: required
language: python
before_install:
- sudo apt-get install python3.5
script:
- python3 check.py $dirPath

before_install 是指在跑脚本之前安装哪些库与包,不同语言需要的运行环境不同,这里我们安装python3来运行我们的check.py$dirPath 比较特殊,是指开发者新增加的文件夹的名字。这里我们假设已经获取了,由变量dirPath指代。

注意,在发起`Pull Request`时,正确的学生仓库组织应如下所示。其中 Fork 是指继承自教师仓库的内容,+ 是指学生自己新添加的被测试项目文件夹。
|-- 20131514 (+)
|-- src
|-- main.py
|-- bin
|-- .travis.yml (Fork)
|-- check.py (Fork)
|-- README.md (Fork)

上面这只是一个简易版本,通过这个配置文件相信读者可以大概明白Travis CI工作的基本原理。接下来本文将从实际使用的CI 配置文件出发,解析其作用。首先,在实际使用时,用于测试的脚本比较复杂,会引入 Shell 中的 if 语句,所以建议将复杂的scripts一起写在一个.sh脚本中,比如我们现在将所有希望执行的脚本都写在py-run.sh脚本中。这个脚本的内容如下所示:

#!/bin/bash
set -v
logfile=log.txt
dir=$(ls -l | grep ^d | awk '/^d/ {print i$NF}' i=`pwd`'/')
curl -X GET "https://api.github.com/repos/$TRAVIS_REPO_SLUG/pulls/$TRAVIS_PULL_REQUEST/commits" > commit.log
cat commit.log
lastCommit=`cat commit.log | jq '.[-1].sha' -r`
shellSuccess=0 for i in $dir
do
exitCode=`python3 check.py $i $logfile`
logContent=$(cat $logfile)
if [ exitCode == 0 ]
then
buildStatus="Success"
else
buildStatus="Fail"
shellSuccess=-1
fi
if [ "$TRAVIS_PULL_REQUEST" != "false" ]
# just pull request get comments
then
curl -H "Authorization: token $GITHUB_TOKEN" -X POST -d "{\"body\":\"**Commit**:$lastCommit\n\n**Build Status**:$buildStatus\n\n**Detail**:$logContent\"}" "https://api.github.com/repos/$TRAVIS_REPO_SLUG/issues/$TRAVIS_PULL_REQUEST/comments"
fi
done
exit $shellSuccess

[ 未完待续 ... ]

使用 Travis CI 实现项目的持续测试反馈的更多相关文章

  1. Travis CI持续集成使用

    用好这个工具不仅可以提高效率,还能使开发流程更可靠和专业化,从而提高软件的价值.而且,它对于开源项目是免费的,不花一分钱,就能帮你做掉很多事情. 一.什么是持续集成? Travis CI 提供的是持续 ...

  2. [转]Travis Ci的最接底气的中文使用教程

    相信大家对Travis Ci已经不再陌生了,Github上已经有大部分的项目已经采用了它. Travis Ci是一个基于晕的持续集成项目,目前已经支持大部分主流语言了,如:C.PHP.Ruby.Pyt ...

  3. iOS中 为 iOS 建立 Travis CI 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 你是否曾经试着为 iOS 项目搭建一台支持持续集成的服务器,从我的个人经验而言,这可不是一个轻松的活.首先需要准备 ...

  4. Travis CI Build Continuous Integration

    什么是持续集成 持续集成(Continuous Integration)是经常合并小的代码更改的实践,而不是在开发周期结束时合并大的更改.目的是通过以较小的增量开发和测试来构建更健康的软件.这就是Tr ...

  5. 【Hexo】使用Hexo+github pages+travis ci 实现自动化部署

    目录 一.说明 二.成品展示 三.前期准备 本地安装 node.js 本地安装 git github 账号 创建仓库 travis ci 账号 四.安装 Hexo 五.使用 hexo 搭建博客 六.部 ...

  6. 利用Travis CI 让你的github项目持续构建

    Travis CI 是目前新兴的开源持续集成构建项目,它与jenkins,GO的很明显的特别在于采用yaml格式,简洁清新独树一帜.目前大多数的github项目都已经移入到Travis CI的构建队列 ...

  7. Travis CI用来持续集成你的项目

    这里持续集成基于GitHub搭建的博客为项目 工具: zqz@ubuntu:~$ node --version v4.2.6 zqz@ubuntu:~$ git --version git versi ...

  8. github Travis CI 持续集成

    一个项目如何保证代码质量是开发中非常重要的环节,对于开源项目来说更是如此,因为开源项目要面对的是来自不同水平开发者提交的代码.所以围绕开源做持续集成(Continuous Integration)变得 ...

  9. 利用Travis CI+GitHub实现持续集成和自动部署

    前言 如果你手动部署过项目,一定会深感持续集成的必要性,因为手动部署实在又繁琐又耗时,虽然部署流程基本固定,依然容易出错. 如果你很熟悉持续集成,一定会同意这样的观点:"使用它已经成为一种标 ...

随机推荐

  1. 初识Spring Boot

    ​ 1.Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,用于简化基于Spring的搭建与开发过程,通过少量的代码创建Spring应用. 2.Spring Boo ...

  2. 单线程泵问题(com操作时间超过60s报错)

    CLR   无法从   COM   上下文   0x197bf0   转换为   COM   上下文   0x197a80,这种状态已持续   60  秒.拥有目标上下文/单元的线程很有可能执行的是非 ...

  3. Windows Server 2016-清理残留域控信息

    本章紧接上文,当生产环境中域控出现问题无法修复以后,一方面我们需要考虑抢夺FSMO角色,另一方面我们需要考虑的问题是清理当前域控的残留信息,以防止残留数据信息导致用户验证或者解析异常等问题.本章讲到如 ...

  4. php笔记(一)php介绍及数据类型

    php 官方手册:http://php.net/manual/zh/ 1.PHP(全称 Hypertext Preprocessor,超文本预处理器的字母缩写)是一种服务器端脚本语言,它可嵌入到 HT ...

  5. IntelliJ IDEA 导入Spring源码

    第一步: 使用git 拉取代码 git 命令: git init    //创建git仓库 git clone  https://github.com/spring-projects/spring-f ...

  6. 近期Python学习笔记

    近期Python 学习笔记--一篇文入门python 作者:Pleiades_Antares(www.cnblogs.com/irischen) 写在前面的话 想学Python已经许久,一年多以前(应 ...

  7. JavaScript getFullYear() 方法

    JavaScript Date 对象 定义和用法 getFullYear() 方法可返回一个表示年份的 4 位数字. 语法 dateObject.getFullYear() 返回值 当 dateObj ...

  8. 发现一种写法,类似callback&&callback()

    与callback&&callback()异曲同工 return data && { title: `To-do (${data.length})`, componen ...

  9. Docker卸载镜像

    Linux服务器Docker卸载某个镜像: 首先输入命令docker images查看当前docker下有多少镜像: 1 [root@iZwz9a191mdam4di3dozk3Z ~]# docke ...

  10. pgsql SQL复杂查询示例

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code   WITH tmp AS(SELECT sum(sessioncount) as v ...