使用 GitHub Actions 創建持續集成測試工作流
已發表: 2022-03-10在為 GitHub 和 Bitbucket 等版本控制平台上的項目做出貢獻時,慣例是主分支包含功能代碼庫。 然後,還有其他分支,其中幾個開發人員可以在主副本上工作,以添加新功能、修復錯誤等。 這很有意義,因為監控傳入的更改將對現有代碼產生的影響變得更加容易。 如果有任何錯誤,可以在將更改集成到主分支之前輕鬆跟踪和修復。 手動檢查每一行代碼以查找錯誤或錯誤可能非常耗時——即使對於一個小項目也是如此。 這就是持續集成的用武之地。
什麼是持續集成 (CI)?
“持續集成 (CI) 是將來自多個貢獻者的代碼更改自動集成到單個軟件項目中的做法。”
— Atlassian.com
持續集成 (CI) 背後的總體思路是確保對項目所做的更改不會“破壞構建”,即破壞現有代碼庫。 根據您設置工作流程的方式,在您的項目中實施持續集成,只要有人對存儲庫進行更改,就會創建一個構建。
那麼,什麼是構建?
構建 - 在此上下文中 - 是源代碼的編譯為可執行格式。 如果成功,則意味著傳入的更改不會對代碼庫產生負面影響,並且可以繼續使用。 但是,如果構建失敗,則必須重新評估更改。 這就是為什麼建議在將項目合併到主代碼庫之前通過在不同分支上處理項目副本來對項目進行更改的原因。 這樣,如果構建中斷,將更容易找出錯誤來自哪裡,並且它也不會影響您的主要源代碼。
“越早發現缺陷,修復它們的成本就越低。”
— David Farley,持續交付:通過構建、測試和部署自動化實現可靠的軟件發布
有多種工具可用於幫助為您的項目創建持續集成。 其中包括 Jenkins、TravisCI、CircleCI、GitLab CI、GitHub Actions 等。在本教程中,我將使用 GitHub Actions。
用於持續集成的 GitHub 操作
CI Actions 是 GitHub 上的一項相當新的功能,可以創建自動運行項目構建和測試的工作流。 工作流包含一個或多個可在事件發生時激活的作業。 此事件可能是推送到 repo 上的任何分支或創建拉取請求。 在我們繼續進行時,我將詳細解釋這些術語。
讓我們開始吧!
先決條件
這是一個面向初學者的教程,所以我將主要從表面上討論 GitHub Actions CI。 讀者應該已經熟悉使用 PostgreSQL 數據庫創建 Node JS REST API、Sequelize ORM,以及使用 Mocha 和 Chai 編寫測試。
您還應該在您的機器上安裝以下內容:
- 節點JS,
- PostgreSQL,
- 新PM,
- VSCode(或您選擇的任何編輯器和終端)。
我將使用我已經創建的名為countries-info-api
的 REST API。 這是一個簡單的API,沒有基於角色的授權(如在撰寫本教程時)。 這意味著任何人都可以添加、刪除和/或更新國家/地區的詳細信息。 每個國家/地區都有一個 id(自動生成的 UUID)、名稱、首都和人口。 為實現此目的,我使用節點JS,Express JS Framework和數據庫的PostgreSQL。
在開始編寫測試覆蓋率測試和持續集成工作流文件之前,我將簡要說明如何設置服務器、數據庫。
您可以克隆countries-info-api
存儲庫以跟進或創建您自己的 API。
使用的技術: Node Js、NPM(Javascript 的包管理器)、Postgresql 數據庫、sequelize ORM、Babel。
設置服務器
在設置服務器之前,我從 npm 安裝了一些依賴項。
npm install express dotenv cors npm install --save-dev @babel/core @babel/cli @babel/preset-env nodemon
我正在使用 express 框架並以 ES6 格式編寫,所以我需要 Babeljs 來編譯我的代碼。 您可以閱讀官方文檔以了解有關它的工作原理以及如何為您的項目配置它的更多信息。 Nodemon 將檢測對代碼所做的任何更改並自動重新啟動服務器。
注意:使用--save-dev
標誌安裝的 Npm 包僅在開發階段需要,並且可以在package.json
文件的 devDependencies 下看到。
我在index.js
文件中添加了以下內容:
import express from "express"; import bodyParser from "body-parser"; import cors from "cors"; import "dotenv/config"; const app = express(); const port = process.env.PORT; app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(cors()); app.get("/", (req, res) => { res.send({message: "Welcome to the homepage!"}) }) app.listen(port, () => { console.log(`Server is running on ${port}...`) })
這將設置我們的API以運行,以便在.env
文件中分配給PORT
變量的原因。 這也是我們將聲明我們不希望其他人輕鬆訪問的變量的地方。 dotenv
npm 包從.env
加載我們的環境變量。
現在,當我在終端中運行npm run start
時,我得到了這個:
如您所見,我們的服務器已啟動並正在運行。 耶!
Web 瀏覽器中的此鏈接https://127.0.0.1:your_port_number/
應返回歡迎消息。 也就是說,只要服務器正在運行。
接下來,數據庫和模型。
我使用 Sequelize 創建了國家模型,並連接到我的 Postgres 數據庫。 Sequelize 是 Nodejs 的 ORM。 一個主要優點是它節省了我們編寫原始 SQL 查詢的時間。
由於我們使用的是 Postgresql,因此可以使用CREATE DATABASE database_name
命令通過 psql 命令行創建數據庫。 這也可以在您的終端上完成,但我更喜歡 PSQL Shell。
在 env 文件中,我們將按照以下格式設置數據庫的連接字符串。
TEST_DATABASE_URL = postgres://<db_username>:<db_password>@127.0.0.1:5432/<database_name>
對於我的模型,我遵循了這個 sequelize 教程。 它很容易理解並解釋了有關設置 Sequelize 的所有內容。
接下來,我將為剛剛創建的模型編寫測試,並在 Coverall 上設置覆蓋範圍。
編寫測試和報告覆蓋率
為什麼要編寫測試? 就個人而言,我相信編寫測試可以幫助您作為開發人員更好地了解您的軟件在用戶手中的預期表現,因為這是一個頭腦風暴的過程。 它還可以幫助您及時發現錯誤。
測試:
有不同的軟件測試方法,但是,在本教程中,我使用了單元測試和端到端測試。
我使用 Mocha 測試框架和 Chai 斷言庫編寫了我的測試。 我還安裝了sequelize-test-helpers
來幫助測試我使用sequelize.define
創建的模型。
測試覆蓋:
建議檢查您的測試覆蓋率,因為結果顯示我們的測試用例是否真正覆蓋了代碼,以及在我們運行測試用例時使用了多少代碼。
我使用了 Istanbul(一個測試覆蓋工具)、nyc(Instabul 的 CLI 客戶端)和 Coveralls。
根據文檔,Istanbul 使用行計數器檢測您的 ES5 和 ES2015+ JavaScript 代碼,以便您可以跟踪您的單元測試如何運行您的代碼庫。
在我的package.json
文件中,測試腳本運行測試並生成報告。
{ "scripts": { "test": "nyc --reporter=lcov --reporter=text mocha -r @babel/register ./src/test/index.js" } }
在此過程中,它將創建一個包含原始覆蓋率信息的.nyc_output
文件夾和一個包含覆蓋率報告文件的coverage
文件夾。 我的倉庫中不需要這兩個文件,所以我將它們放在.gitignore
文件中。
現在我們已經生成了一份報告,我們必須將其發送給 Coveralls。 關於 Coveralls(和其他覆蓋工具,我假設)的一件很酷的事情是它如何報告您的測試覆蓋率。 覆蓋範圍是逐個文件分解的,您可以查看相關覆蓋範圍、覆蓋和遺漏的行以及構建覆蓋範圍的變化。
首先,安裝工作服 npm 包。 您還需要登錄工作服並將存儲庫添加到其中。
然後通過在您的根目錄中創建一個coveralls.yml
文件來為您的 javascript 項目設置工作服。 此文件將保存您從設置部分獲得的repo-token
,用於您的工作服 repo。
package.json 文件中需要的另一個腳本是覆蓋腳本。 當我們通過 Actions 創建構建時,這個腳本會派上用場。
{ "scripts": { "coverage": "nyc npm run test && nyc report --reporter=text-lcov --reporter=lcov | node ./node_modules/coveralls/bin/coveralls.js --verbose" } }
基本上,它將運行測試、獲取報告並將其發送到工作服進行分析。
現在到本教程的重點。
創建 Node JS 工作流文件
至此,我們已經設置了將在 GitHub Action 中運行的必要作業。 (想知道“工作”是什麼意思?繼續閱讀。)
GitHub 通過提供起始模板使創建工作流文件變得容易。 如操作頁面所示,有幾個工作流模板服務於不同的目的。 對於本教程,我們將使用Node.js工作流程(Github已經暗示了)。
您可以直接在GitHub上編輯文件,但我將手動在本地repo上創建文件。 包含node.js.yml
文件的文件夾.github/workflows
將位於根目錄中。
此文件已包含一些基本命令,第一個註釋解釋了他們的所作所為。
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
我會對它進行一些更改,以便除了上述評論之外,它還運行覆蓋範圍。
我的.node.js.yml
文件:
name: NodeJS CI on: ["push"] jobs: build: name: Build runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run build --if-present - run: npm run coverage - name: Coveralls uses: coverallsapp/github-action@master env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} COVERALLS_GIT_BRANCH: ${{ github.ref }} with: github-token: ${{ secrets.GITHUB_TOKEN }}
這是什麼意思?
讓我們分解一下。
-
name
這將是您的工作流(NodeJS CI)或作業(構建)的名稱,GitHub 將在您的存儲庫的操作頁面上顯示它。 -
on
這是觸發工作流的事件。 我文件中的那一行基本上是告訴 GitHub 每當向我的倉庫推送時觸發工作流。 -
jobs
一個工作流可以包含至少一個或多個作業,每個作業都在runs-on
指定的環境中運行。 在上面的文件示例中,只有一個作業運行構建並運行覆蓋,它在 Windows 環境中運行。 我還可以將它分成兩個不同的工作,如下所示:
更新了 Node.yml 文件
name: NodeJS CI on: [push] jobs: build: name: Build runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run build --if-present - run: npm run test coverage: name: Coveralls runs-on: windows-latest strategy: matrix: node-version: [12.x, 14.x] steps: - uses: coverallsapp/github-action@master env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} with: github-token: ${{ secrets.GITHUB_TOKEN }}
-
env
這包含可用於工作流中的所有或特定作業和步驟的環境變量。 在覆蓋作業中,您可以看到環境變量已被“隱藏”。 它們可以在你的 repo 的 secrets 頁面中的設置下找到。 -
steps
這基本上是運行該作業時要採取的步驟列表。 -
build
作業做了很多事情:- 它使用簽出操作(v2 表示版本)從字面上簽出您的存儲庫,以便您的工作流程可以訪問它;
- 它使用 setup-node 操作來設置要使用的節點環境;
- 它運行我們的 package.json 文件中的安裝、構建和測試腳本。
-
coverage
這使用了一個coverallsapp 操作,將您的測試套件的LCOV 覆蓋率數據發佈到coveralls.io 進行分析。
我最初推送了我的feat-add-controllers-and-route
分支並忘記將來自 Coveralls 的 repo_token 添加到我的.coveralls.yml
文件中,所以我得到了你可以在第 132 行看到的錯誤。
Bad response: 422 {"message":"Couldn't find a repository matching this job.","error":true}
一旦我添加了repo_token
,我的構建就能夠成功運行。 如果沒有此令牌,CoverAlls將無法正確報告我的測試覆蓋率分析。 我們的github動作ci在推送到主分支之前指出了錯誤。
注意:這些是在我將工作分成兩份之前拍攝的。 此外,我能夠在終端上看到覆蓋摘要和錯誤消息,因為我在覆蓋腳本末尾添加了--verbose
標誌
結論
我們可以看到如何為我們的項目設置持續集成,並使用 GitHub 提供的操作集成測試覆蓋率。 還有很多其他方法可以調整它以適應您的項目需求。 儘管本教程中使用的示例 repo 是一個非常小的項目,但您可以看到即使在更大的項目中持續集成也是如此重要。 現在我的工作已經成功運行,我很自信與我的主要分支合併分支。 我仍然建議您在每次運行後仔細閱讀這些步驟的結果,以查看它是否完全成功。