如何设置Github Actions为项目实现自动化测试?

持续集成(CI)是软件开发过程中的一个重要部分,在这个过程中,共享的代码存储库会由于将团队成员的新工作集成到其中而不断地发生变化。

为了确保代码的高质量并减少潜在的错误,每次集成通常由一个自动化的构建和测试过程来验证。

在本文中,我们将学习如何使用Github Actions设置该过程,从而使项目自动构建和运行单元测试。该项目使用Golang编写,并使用PostgreSQL作为其主要数据库。

一、Github Actions如何运作

Github Action是Github提供的一项服务,具有与其他CI工具(如Jenkins,Travis或CircleCI)相似的功能。

Workflow

为了使用Github Actions,我们必须定义一个工作流程(workflow)。 工作流基本上是由一个或多个作业组成的自动化过程。它可以通过3种不同的方式触发:

  • 通过Github存储库上发生的事件
  • 通过设置一个重复的时间表
  • 手动单击repository UI上的run workflow按钮
1

要创建工作流,我们只需要将.yml文件添加到存储库的.github/workflows文件夹中。例如,这是一个简单的工作流文件ci.yml

name: build-and-test

on:
  push:
    branches: [ master ]
  schedule:
    - cron:  '*/15 * * * *'

jobs:
  build:
    runs-on: ubuntu-latest

这个工作流的名称为build-and-test。我们可以使用on关键字定义如何触发它。

在这个流程中,有一个事件将在更改被推送到master分支时触发工作流,以及还有一个每15分钟运行一次工作流的定期调度触发器。

然后,在工作流yaml文件的jobs部分中定义要运行的作业列表。

Runner

为了运行jobs,我们必须为每个job指定一个runnerrunner是侦听可用作业的服务器,并且一次只能运行一个作业。

我们可以直接使用Github托管的runner,或者指定我们自己的自托管runner。

runners将运行这些jobs,然后将其进度,日志和结果报告回Github,这样我们就可以很容易地在存储库的UI上进行检查。

2

我们使用run-on关键字指定要使用的runner,如上图那样。在此示例工作流程中,将使用Github托管的运行程序来安装Ubuntu最新版本。

Job

job是在同一runner上执行的一组步骤。

3

通常,工作流(workflow)中的所有job都是并行运行的,除非你有一些相互依赖的job,否则它们将按顺序运行。

4
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v2
      - name: Build server
        run: ./build_server.sh
  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: ./test_server.sh

在此示例中,有2个job

  • 第一个是build,它包含2个步骤:检查代码和构建服务器。

  • 第二个是test,它将运行应用程序的测试。

    在这里,使用need关键字来表示test依赖于build,因此只有在应用程序成功构建后才能运行测试。

    这个test只有一个步骤,运行test_server.sh脚本。

Step

step是在每个job中一个接一个连续运行的单个任务。 一个步骤可以包含1个或多个action。

action基本上是一个独立的命令,例如上面的run test_server.sh命令。 如果一个step包含多个action,它们将按顺序运行。

关于action的一个有趣的事情是它可以被重复利用。 因此,如果有人已经编写了我们需要的github action,则可以在workflow中使用它。

让我们看一下这个例子:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v2
      - name: Build server
        run: ./build_server.sh

在这里,使用steps关键字列出要在jobs中运行的所有步骤。

第一步是从Github上将代码转移到runner服务器。要做到这一点,只需使用Github actions团队已经编写好的Github actions checkout@v2

第二步是构建应用服务器。在本例中,我们提供了自己的操作,它只是运行存储库中的build_server.sh脚本。

总结

在开始编码之前,让我们做一个简短的总结:

  • 我们可以通过3种方式触发工作流(workflow):事件,调度计划或手动。
  • 一个工作流由一个或多个任务(jobs)组成。
  • 一个任务由多个步骤(steps)组成。
  • 每个步骤可以有1个或多个动作(actions)。
  • 工作流(workflow)中的所有任务(jobs)通常并行运行,除非它们相互依赖,在这种情况下,它们是串行运行的。
  • 每个任务(jobs)由特定服务器(runner)单独运行。
  • 服务器(runner)会将进度,日志和任务结果报告回github。 我们可以直接在Github存储库的UI上检查它们。

二、实战,为Golang+Postgres设置workflow

结合https://github.com/shisuizhe/simple-bank食用。

现在让我们学习如何为Golang应用程序设置一个真实的workflow,以便它可以连接到Postgres,并在每当git push到github时运行所有单元测试。

使用workflow模板

在Github仓库中,选择Actions标签。

Github知道我们的项目主要是用Go编写的,因此建议我们为Go设置工作流程。 让我们单击此设置按钮。

可以看到,存储库下的.github / workflows文件夹下将创建一个新文件go.yml:

name: Go

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:
    name: Build
    runs-on: ubuntu-latest
    steps:

    - name: Set up Go 1.x
      uses: actions/setup-go@v2
      with:
        go-version: ^1.13
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: Get dependencies
      run: |
        go get -v -t -d ./...
        if [ -f Gopkg.toml ]; then
            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
            dep ensure
        fi

    - name: Build
      run: go build -v .

    - name: Test
      run: go test -v .

我们可以在这里直接使用Github编辑器编辑这个文件。但是,我更喜欢先将文件添加到本地项目目录下,然后在推送到Github之前在本地编辑好它。

创建一个workflow的yaml文件

进入项目根路径,创建一个新文件夹.github/workflows

mkdir -p .github/workflows

然后在这个文件夹中为工作流程创建一个新的yaml文件。 可以随意命名,这里,我命名为ci.yml。

touch .github/workflows/ci.yml

将github提供的模板内容拷贝到ci.yml,并做一些修改。

配置触发事件

定义可以触发此工作流程的事件。 通常,只要有内容修改被push到master分支,或者有合并到master分支的拉取请求,我们都希望运行测试。

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

还有很多其他事件可以配置。请参考Github Actions文档。

设置任务

接下来设置jobs。 在Github提供的模板中,只有1个job名为build,其运行在Ubuntu runner上。我把它重命名为test,因为这个名称表示它的主要目的。

jobs:

  test:
    name: Test
    runs-on: ubuntu-latest

这个任务job还有几步需要做:

  • 步骤1:安装Go环境。

    在runner服务器上安装go,在这一步中,只需要使用现有的Github操作,即setup-go @ v2

    steps:
    
    - name: Set up Go 1.x
      uses: actions/setup-go@v2
      with:
    	go-version: ^1.15
      id: go
    

    使用with关键字为该操作提供输入参数。 在这里,要求它使用特定版本的Go,例如1.15版。

    id字段只是此步骤的唯一标识符。 如果要在其他上下文中引用此步骤,则可能需要它。

  • 步骤2:转移代码

    将这个存储库的代码转移到runner服务器上。 为此,我们还重复使用现有操作:checkout @ v2。

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2
    
  • 步骤3:获取依赖关系

    获取项目正在使用的所有依赖项或外部软件包。

    - name: Get dependencies
      run: |
        go get -v -t -d ./...
        if [ -f Gopkg.toml ]; then
            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
            dep ensure
        fi
    

    但是,在这里,我们不需要这一步,因为当构建应用程序或运行测试时,go mod会自动下载缺少的库。 因此,将其删除!

    构建步骤也是不必要的,因为应用程序将运行go test时会自动构建。

    - name: Build
      run: go build -v .
    
  • 步骤4:运行测试

    最后一步是运行单元测试。 为此,我们已经在Makefile中定义了一个make test命令。 因此,在此步骤中要做的就是调用它:

    - name: Test
      run: make test
    

将workflow推送到github

至此,我们完成了CI workflow的第一个基本版本:

name: ci-test

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  test:
    name: Test
    runs-on: ubuntu-latest

    steps:

    - name: Set up Go 1.x
      uses: actions/setup-go@v2
      with:
        go-version: ^1.15
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: Test
      run: make test

由于我们尚未设置Postgres数据库,因此它可能无法正常工作。 但让我们将其推送到Github上看看其运行方式:

git add .
git commit -m "init ci workflow"
git push origin master

现在,回到Github存储库页面并选择Actions。所有步骤(steps)均在右侧列出。Setup jobSetup GoCheckout code步骤成功完成,这里,Test虽然已经完成,但是失败了。

打开步骤Test,查看日志可以看到:

正如日志中看到的,代码不能连接到Postgres的端口5432,因为我们还没有在工作流中设置它。现在就开始吧!

添加Postgres服务

让我们搜索github action postgres,然后打开有关创建Postgres服务容器的Github Action官方文档页面。

从上图可以看到Postgres被声明为该job的外部服务。 让我们复制此代码块并将其粘贴到我们的工作流文件中。

因此,我们使用services关键字来指定要与我们的job一起运行的外部服务列表。在本例中,只需要1个服务,即Postgres。

并且,由于项目中使用的是Postgres版本12,因此我们将此Docker映像名称设置为postgres:12。 可以在Docker Hub上查看此Postgres镜像的可用版本和标签。

services:
  postgres:
    image: postgres:12
    env:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: 123456
      POSTGRES_DB: simple_bank
    ports:
      - 5432:5432
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

接下来,我们需要为访问数据库的认证设置一些环境变量。

请记住,我们在本地Postgres容器中使用user="postgres",password="123456"和database="simple_bank"。 因此,我们在此处为CI工作流程设置相同的值。

options选项非常重要,因为runner服务器使用它来检查Postgres是否已成功启动,以便它知道何时运行工作流程中的后续步骤。

添加运行数据库迁移步骤

现在已经定义好了Postgres服务,但是为了让测试成功运行,还需要运行db迁移,为我们的应用程序创建正确的数据库模型。

因此,在Check out code ...步骤之后定义一个新步骤。 名称为Run migrations。 它唯一需要做的就是运行make migrationup

好了,现在让我们尝试将这个新的工作流更改推送到Github,看看会发生什么。

在图中箭头处可以看到一条日志说postgres service is healthy。Setup Go步骤也成功。然后它转移新的代码到runner。

但是,数据库迁移失败了,因为没有安装golang-migrate CLI工具来运行迁移。

安装 golang-migrate 工具

因此,让我们搜索golang migrate,然后打开此Github页面文档。

根据您使用的操作系统,有几个选项。 因为我们正在使用Ubuntu作为runner,因此我将复制此curl命令以下载构建迁移的二进制文件。

curl -L https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$platform-amd64.tar.gz | tar xvz

现在,在工作流程中,添加一个新的Install golang-migrate步骤在Run migrations之前。 然后,在run操作中,粘贴curl命令。

- name: Install golang-migrate
  run: curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz

它将下载zip文件,并解压得到名为migration .linux-amd64的迁移二进制文件。 现在,为了使migration命令起作用,我们必须将该二进制文件移动到/usr/bin文件夹中。

因为这一步骤包含不止一个命令。在这里可以使用管道符|来指定多行命令。让我们将这个move命令添加到步骤中:

- name: Install golang-migrate
  run: |
    curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
    sudo mv migrate.linux-amd64 /usr/bin/
    which migrate

请注意,只有超级用户才能更改/usr/bin文件夹的内容,因此我们必须使用sudo运行此命令。

还增加了1个命令:which migrate,仅用于检查migrate CLI二进制文件是否已成功安装,是否可以在runner中使用。

现在,workflow是完美配置了吗?如果就这一样push到github的话,再次测试还是会失败。

因为项目中的Makefile指定了迁移命令的二进制文件名为migrate,而不是上面设置的migrate.linux-amd64,所以还要做修改:

- name: Install golang-migrate
  run: |
    curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
    sudo mv migrate.linux-amd64 /usr/bin/migrate
    which migrate

好了,让我们重新看看完整的workfolw配置文件:

name: ci-test

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  test:
    name: Test
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:12
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: 123456
          POSTGRES_DB: simple_bank
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:

    - name: Set up Go 1.x
      uses: actions/setup-go@v2
      with:
        go-version: ^1.15
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: Install golang-migrate
      run: |
        curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
        sudo mv migrate.linux-amd64 /usr/bin/migrate
        which migrate

    - name: Run migrations
      run: make migrateup

    - name: Test
      run: make test

提交测试:

Nice!最终我们的CI测试工作流程成功运行。

通过编写一个Github Action工作流程来运行需要连接到外部Postgres服务的Golang单元测试,让我们已经了解了持续集成。

Github Actions还可以做更多的事情。 查看其官方文档以了解有关它们的更多信息。

That's all,see you next time.

posted @ 2020-09-29 16:17    阅读(2036)  评论(0编辑  收藏  举报