使用 Travis CI 实现项目的持续测试反馈
[篇幅较长,10.15前补充完毕,如希望探索可直接移步Github仓库:https://github.com/SivilTaram/CITest]
在编程课中,我们可以使用成熟的在线评测系统来测试某个代码块或文件在功能实现上的正确性。但在软件工程课中,对项目的自动测试仍然是一个有挑战性的问题。一个比较复杂的软件工程项目往往由多个文件组成,开发者可能会调用不同的第三方库函数,使用不同的编译环境(比如Mac/Linux/Windows),这些因素导致了自动测试项目的复杂性。
早在布置数独项目给福州大学的学生时,我就意识到了自动测试项目不是一个可以一锤子做完的买卖。核心测试的代码并不难写,例如数独生成的项目,核心就是检查被测试程序生成的数独棋盘是否符合规范。按照我的经验,真正阻碍许多助教独立开发自动测试项目的因素主要是三点:
- 助教需要写一个爬虫自动爬取所有学生的Github仓库地址,将它们克隆到本机,并把每个项目仓库存储目录都重命名为学生学号以记录测试成绩。这一点要求助教能分析一些基本的网络请求,并利用代码构造请求。
- 助教要在布置项目时对学生的开发进行一些约束,并写一个带异常处理的测试程序来处理不同约束被破坏的情况。这就意味着,自动测试项目除了能够评测正确情况外,还要对一些约束不满足的情况给出合理的报错,比如说
规定的 src 文件夹不存在。这一点要求助教完成一个鲁邦、友好又准确无误的测试程序,有些时候测试程序甚至比项目本身更复杂。 - 如果在布置项目时要求学生提交源码的同时也提交可执行程序(比如.exe),助教无法从源头上保证学生提交的可执行程序编译于他自己的代码;但如果不要求学生提交可执行程序,即使约束了指定编译环境与测试命令,助教也会面临大部分项目编译失败的风险(这是由项目复杂度引起的)。这一点要求助教在本机安装必要的编译环境,每个助教都需要做一遍环境配置,很慢很麻烦。
其实这三点问题多多少少都与测试策略的选择有关:目前软件工程课程中普遍采用后处理的自动测试策略,即学生完成开发后助教才进行测试,再过一定时间学生才能知道自己项目的错误。 但在实践中,大部分学生是无法一次通过测试,不能通过的原因往往不是代码没写好,而是因为他们遗漏或错误解读了作业细节。这种源于双方认知差异而导致的问题也不一定就是学生们的失误,也可能是老师或助教没有明确指定一些看似显然的约束条件。为了避免这种问题的产生,现有的测试流程中需要加入持续测试反馈的环节。结合前人的经验,本文制定了一套新的测试流程,其中的关键就在于 Git 提供的 Pull Request 与 Continuous Integration (持续集成,下面简称为CI) 工具。本文下面将分三个部分来说明新的测试流程,首先是前后测试流程的对比,接着是一个基于Travis CI与Github搭建的持续测试的最小工作示例,最后是教各位助教定制化自己的测试脚本。
测试流程
在传统软件工程课程的测试中,测试流程如下图所示:

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

- 发布仓库: 教师发布一个公开仓库,学生 Fork。
- 静态检查: 学生完成开发,发起 Pull Request。CI 检测到新的 Pull Request 发起,运行教师预先设定的静态检查程序。
- 持续反馈: 完成静态检查后, CI 自动在相应的 Pull Request 中追加评测信息(包括是否通过初步检查,错误日志等)。学生看到评测信息后更正程序,回到第 2 步。
- 持续编译[可选]: 完成静态检查后,CI 运行教师预先设定的编译脚本,把由学生仓库编译得到的可执行程序补充 commit 到相应的文件夹下。
- 动态测试: 在作业截止日期后,助教运行动态测试程序,测试可执行程序在相应输入下是否可以得到期望输出,得到不同测试样例下的得分。
新的测试流程对助教显得更友好一些:Pull Request 的仓库收集方式避免了爬虫的工作量,CI 的自动反馈帮助学生持续地获得反馈,也使得助教不再需要定时反馈项目错误信息。同时,这种测试流程可以保证 git 仓库中的可执行文件一定是从学生自己的源代码编译得到的,不需要助教在本机安装任何编译器。
最小工作范例
接下来本文将展示一个基于上述测试流程的最小工作范例,主要用到的相关知识如下:
- [x] Git Command
- [x] Programming (C++/Python/Java ...)
- [ ] Linux Shell Script
- [ ] Github Rest Api v3
- [ ] Customize Build in Travis CI
前两项是必须学会的背景知识,如果不会的话请先熟悉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 实现项目的持续测试反馈的更多相关文章
- Travis CI持续集成使用
用好这个工具不仅可以提高效率,还能使开发流程更可靠和专业化,从而提高软件的价值.而且,它对于开源项目是免费的,不花一分钱,就能帮你做掉很多事情. 一.什么是持续集成? Travis CI 提供的是持续 ...
- [转]Travis Ci的最接底气的中文使用教程
相信大家对Travis Ci已经不再陌生了,Github上已经有大部分的项目已经采用了它. Travis Ci是一个基于晕的持续集成项目,目前已经支持大部分主流语言了,如:C.PHP.Ruby.Pyt ...
- iOS中 为 iOS 建立 Travis CI 韩俊强的博客
每日更新关注:http://weibo.com/hanjunqiang 新浪微博! 你是否曾经试着为 iOS 项目搭建一台支持持续集成的服务器,从我的个人经验而言,这可不是一个轻松的活.首先需要准备 ...
- Travis CI Build Continuous Integration
什么是持续集成 持续集成(Continuous Integration)是经常合并小的代码更改的实践,而不是在开发周期结束时合并大的更改.目的是通过以较小的增量开发和测试来构建更健康的软件.这就是Tr ...
- 【Hexo】使用Hexo+github pages+travis ci 实现自动化部署
目录 一.说明 二.成品展示 三.前期准备 本地安装 node.js 本地安装 git github 账号 创建仓库 travis ci 账号 四.安装 Hexo 五.使用 hexo 搭建博客 六.部 ...
- 利用Travis CI 让你的github项目持续构建
Travis CI 是目前新兴的开源持续集成构建项目,它与jenkins,GO的很明显的特别在于采用yaml格式,简洁清新独树一帜.目前大多数的github项目都已经移入到Travis CI的构建队列 ...
- Travis CI用来持续集成你的项目
这里持续集成基于GitHub搭建的博客为项目 工具: zqz@ubuntu:~$ node --version v4.2.6 zqz@ubuntu:~$ git --version git versi ...
- github Travis CI 持续集成
一个项目如何保证代码质量是开发中非常重要的环节,对于开源项目来说更是如此,因为开源项目要面对的是来自不同水平开发者提交的代码.所以围绕开源做持续集成(Continuous Integration)变得 ...
- 利用Travis CI+GitHub实现持续集成和自动部署
前言 如果你手动部署过项目,一定会深感持续集成的必要性,因为手动部署实在又繁琐又耗时,虽然部署流程基本固定,依然容易出错. 如果你很熟悉持续集成,一定会同意这样的观点:"使用它已经成为一种标 ...
随机推荐
- SQL Server基础之登陆触发器
虽然同表级(DML)触发器和库级(DDL)触发器共顶着一个帽子,但登陆触发器与二者有本质区别.无论表级还是库级,都是用来进行数据管理的,而登陆触发器是纯粹的安全工具. 登陆触发器只响应LOGON事件, ...
- 洗礼灵魂,修炼python(74)--全栈项目实战篇(2)——前期准备之详解虚拟机下安装ubuntu,基本配置,远程访问
如果上一篇我转发的关于ubuntu的博文,你看完觉得还没准备好,那么,本篇从最基础的开始,安装虚拟机到正常使用ubuntu 虚拟机 1.什么是虚拟机 虚拟机(Virtual Machine)指通过软件 ...
- LeetCode算法题-Best Time to Buy and Sell Stock
这是悦乐书的第172次更新,第174篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第31题(顺位题号是121).假设有一个数组,其中第i个元素是第i天给定股票的价格.如果 ...
- mysql统计一年12月的数据
效果图: select end) as 一月份, end) as 二月份, end) as 三月份, end) as 四月份, end) as 五月份, end) as 六月份, end) as 七月 ...
- Java中使用elasticsearch搜索引擎实现简单查询、修改等操作-已在项目中实际应用
以下的操作环境为:jdk:1.8:elasticsearch:5.2.0 maven架包下载坐标为: <dependency> <groupId>org.elasticsear ...
- Python开发【第二篇】:基本数据类型
运算符 1.算数运算 2.比较运算 3.赋值运算 4.逻辑运算 5.成员运算 基本数据类型 所有对象所具备的方法都保存在类中.对象和类的关系,举个例子:哺乳动物是类:此类下有两个对象,一个为狗.一个为 ...
- Spring将Bean导入IOC容器
@Import 注解可以普通类导入到 IoC容器中. 想要让一个普通类接受 Spring 容器管理,有以下方法 使用 @Bean 注解 使用 @Controller @Service @Reposit ...
- UVA12188-Inspector's Dilemma(欧拉回路+连通性判断)
Problem UVA12188-Inspector's Dilemma Time Limit: 3000 mSec Problem Description In a country, there a ...
- EL表达式运算符使用
EL表达式关系运算符的使用 == eq 等于 != ne 不等于 > gt 大于 < lt 小于 >= ge 大于等于 <= le 小于等于 举例说明 > 或者 gt, ...
- 5-servlet简介
一.servlet1.是什么:java程序来处理页面请求和响应2.实现方式: a.实现Servlet接口 b.继承HttpServlet类 3.步骤: a.创建一个java程序实现Servlet或者继 ...