使用 GitLab Runner 完成 Django CI
“Code without tests is broken by design.” - Jacob.
写应用,部署应用,很重要的一个环节便是测试(然后就是所谓的 CI/CD( Continuous Integration and Continuous Delivery)),可能许多初学者写软件/或者 Web App 会经历几个阶段(我也是这么过来的):
- 直接
xxx startproject yyy
然后开始写,每天存盘 - 学会使用 GitHub 之后开始每天晚上将自己写的代码推送到 GitHub 作为一个备份
- 学会在操作失误之后使用
git reset HEAD --hard
回滚到上一个可用的状态 - 和一些同学在 GitHub 上协作,然后多个人修改了同一个文件之后遇到 Conflict ,开始扯皮(我不是说了你不要改这个奇怪的地方嘛?你等会,我先把这个推送了来)
- 学会使用 Branches 减少扯皮次数,并且服务端脚本通过定期
pull
GitHub 上的代码来完成功能的上线(涉及到数据库修改的就先down
一下) - 发现用户总是会先比自己发现代码上的 Bug
- 开始学习写一些测试
那么问题来了:
Django 怎么写测试
这个基本可以参考 Django 官方文档,如果希望自己的代码看上去有条理一点就不要所有测试全部丢在 tests.py
里面,可以多创建几个文件,比如 test_static_views.py
,test_auth_functions.py
之类,毕竟:
The default
startapp
template creates atests.py
file in the new application. This might be fine if you only have a few tests, but as your test suite grows you’ll likely want to restructure it into a tests package so you can split your tests into different submodules such astest_models.py
,test_views.py
,test_forms.py
, etc. Feel free to pick whatever organizational scheme you like.
假设我们需要一个非常简单的测试,测试一下首页是否可以工作,可以如何来写呢?
from django.test import TestCase, Client
# These are tests for static pages and login/register function.
class ViewTests(TestCase):
def test_index(self):
response = self.client.get('/')
self.assertEqual(response.status_code,200)
这样假设访问首页能获得一个 200 的返回(而不是 403,415 啥的)的话测试就通过啦,本地先验证一下,使用 python manage.py test <app 的名字>
来测试某一个 app 下的测试,返回结果可能如下所示:
(env) ➜ app git:(master) python manage.py test <app 的名字>
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK
Destroying test database for alias 'default'...
没问题了?好的,我们下一步看看如何在 git push
之后让服务器帮我们跑一下测试(之后还可以加入一些,测试通过后自动部署的操作)。
GitLab && GitLab Runner
为了(更加贴近 LeetCode 技术栈|希望尝试一下 Python 的 Web 框架)
,这里使用 GitLab 作为项目托管,并且分别演示如何使用 GitLab.com 官方的 Shared Runner (基于 GCE)和使用自己的 Runner (放在自己的 Docker 中)来运行测试(毕竟官方那个… 好像有点慢)。
使用 GitLab Shared Runner
要在 GitLab 上运行自己的测试,我们需要在项目的根目录下创建一个名为 .gitlab-ci.yml
的文件(当然,也可以不放在根目录下,不过这样的话需要自己指定一下),内容可能如下:
stages:
- test
test:
stage: test
script:
- apt-get update -qy
- apt-get install -y python3-dev python3-pip
- cd app # 我的应用放在了 app 目录下,所以需要先 cd 进去
- cp leetcode-sample/settings.py.example leetcode-sample/settings.py # 我的 settings.py 被 gitignore 了,防止暴露一些敏感信息
- pip3 install -r requirements.txt
- python3 manage.py test
然后直接 push
就可以了,一般来说,你可以看到如下的结果:
看日志的话最后几行应该如下:
$ python3 manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK
Destroying test database for alias 'default'...
System check identified no issues (0 silenced).
Job succeeded
和我们想要的一样,不错~
使用自己的 GitLab Runner
安装 Docker 后运行 GitLab Runner,可以参考我的 n0vad3v/dockerfiles 仓库下的 gitlab-runner ,通过 docker-compose up -d
启动之后通过 docker ps
获取到自己的 CONTAINER ID
,然后进入容器进行配置:
$ docker exec -it <Container ID> gitlab-runner register
配置方法参考:Registering Runners | GitLab 即可,如果没有在 .gitlab-ci.yml
中指定的话,会默认使用 ruby:2.7
的包,这里由于我们主要是 Django 项目,所以可以指定一个 python:3.7
之类的 image,完成之后应该可以在自己的项目设置中看到自己的 Runner:
不过如果你像我这样随意打 Tag 的话容易提交了没法运行,并且报错:
This job is stuck because the project doesn’t have any runners online assigned to it.
这里一个简单粗暴的方法是勾选"Indicates whether this runner can pick jobs without tags",即可~
多写测试,尽量测试驱动开发,不仅可以减少上来就写代码而导致的后期的麻烦,还可以在一些特殊场合(比如面试的时候)给他人一个比较良好的印象。