pre-commit hook:测试的最佳位置
简介
你或者你的同事、朋友是否有遇到过提交的代码无法运行的问题?
可能是某个 class 中某个语句缺少了结尾分号,导致了在部署的时候编译出错。
他也可能是由于系统的某个微小的特性改变了,导致单元测试不通过。
或者更加严重的,这个问题并没有能够被及时发现,而是用户那边提出来的。
在我及我的朋友身上已经发生过几次这种事情了,就是在提交 merge 或者 rebase 到 master 分支之前,并没有经过编译测试,就直接将代码修改提交到了服务器上。这并不安全,并且是应该极力避免的。
如何避免呢?下面我将向你介绍 pre-commit 这个 git hooks(Git 钩子函数)。
Git hooks
Git hooks 其实就是一个 shell 函数,它可以在特定的操作发生前/后被执行。你可以在你项目的 .git/hooks 路径下找到,比如 .git/hooks/pre-commit.sh.sample。如果要让这个脚本生效,我们需要将该文件末尾的 .sample 后缀去掉。
Pre-commit 这个钩子,从名字也能看出,它将会在你每次提交 commit 之前执行,比如 git commit -m “初始化提交”
。这个脚本便会在你提交之前先执行。如果这个脚本执行到最后的退出代码为 0 时,就代表执行成功,然后 commit 将会被接着执行,但如果这个脚本返回了非 0 的退出代码,则代表这个脚本执行失败了(比如编译失败)则后面的 commit 操作将不会再被执行。
以下便是初始化时 pre-commit.sample 脚本的内容:
1 |
|
拒绝不合格的 commit 提交
通过 pre-commit 这个 Git hook,我们能够很轻易地实现过滤不想要的提交。当我们的脚本退出代码为 1 的时候,他检测到有错误发生,然后 pre-commit 脚本执行失败。在这种情况下,这个 commit 将不会被提交,当然他也不会出现在任何分枝上。
我们可以使用这个钩子来创建一个测试控制器脚本。这个脚本应该构建我们的项目,然后运行所有的测试代码,来保证当前改变并不会影响应用的行为,最好再测试一下本应用能否正常启动。
我最近就创建了一个 pre-commit 脚本,它能够运行我项目的测试控制代码。
1 |
|
首先,这个脚本需要运行一个 bash 脚本 script/testharness.sh。
然后,通过 if [ $1 -ne 0 ]; then
检查上一个脚本执行的退出代码是否为 0,即代表成功(0代表失败)
我的 pre-commit 脚本看起来比较简单。真正核心的指令都在 testharness.sh 这个脚本里面。
testharness.sh
维基百科上说
在软件开发中,test harness 或者自动测试化框架是一个软件以及测试数据和配置项的组合,用于软件单元测试,具体则是通过验证它的运行状态以及监控它的行为和输出来测试一个软件单元。
我想编写一个让我不再提交错误代码的脚本。我也是一个普通人,我不可能在我每次提交前都仔细认真地检查每行代码,所以,总是可能发生错误。
我只用了五个步骤,来帮助我部署一个整洁的没有低级错误的代码到我的代码仓库中:
- 编译项目
- 运行单元测试和集成测试
- 运行 Web 应用
- 在运行着的 Web 程序上执行自动化测试
- 关闭 Web 应用并清理现场
我写了一个 testharness.sh 来自动执行这个任务而不是每次都手动地去执行。
1 |
|
首先,我编译了整个项目。
第二步,运行单元测试和集成测试。因为我是用的 maven 管理项目,所以只需要执行 mvn clean packge
即可。
紧接着,这个项目就已经存在于 /target 目录下了。所以,在第三步中,我执行 runApp 来运行这个 web 应用,并且等待它的执行结果。
第四步,运行自动化测试,在第三步的时候就触发了一系列的请求。自动化测试可能会耗费不少的时间(甚至几分钟)所以这是一个去厨房弄杯咖啡的好时机。
第五步,tearDown 只是通过 kill 掉程序对应的进程来关闭这个 Web 应用,然后清理现场。清理现场这个函数其实也可能会在其他几步中被调用(在发生错误后,退出程序前)。在清理完成后,即退出脚本。
现在让我们试试提交一些改动,从而触发自动测试等的执行。
Test harness 的使用
正如你所看到的一样,脚本执行成功,因此 commit 也顺利提交到了我的主分支。
测试脚本执行其实超过了一分钟。如果我们时间比较紧急,比如我们必须尽快地修复生产环境的 Bug,我们可以用 -n
或者 —-no-verify
命令跳过所有的钩子。比如
1 | git commit -nm ”快速修复” |
总结
正如上文所述,git hooks 非常有用。我这儿仅仅是列举了其中的一个, pre-commit,能够避免开发者提交不符合的代码。
通过将测试框架脚本和 pre-commit 结合,便能够确保你的提交不会在编译的时候就因为一些低级错误挂掉,或者提交后改变了应用的行为。
这个策略让我在日常的代码编写中受益良多。