返回文章列表

Jenkins 自動化測試微服務架構

本文探討如何使用 Jenkins 在微服務架構中實施自動化測試策略,涵蓋單元測試、程式碼覆寫率分析、安全性掃描以及平行測試等關鍵環節。文章以 Python、Go 等程式語言的微服務為例,展示如何在 Docker 容器內執行測試,並將測試結果整合到 Jenkins Pipeline中,以提升軟體交付效率和品質。

Web 開發 DevOps

隨著微服務架構的普及,如何在持續整合流程中有效地執行自動化測試成為一大挑戰。本文將介紹如何利用 Jenkins 和 Docker 建立一個健全的自動化測試流程,涵蓋單元測試、程式碼覆寫率分析、依賴套件安全性檢查等導向。我們將以 Python 和 Go 語言撰寫的微服務為例,示範如何在 Docker 容器中執行測試,並透過 Jenkins Pipeline整合測試結果,生成報告,並及早發現程式碼缺陷,確保軟體品質。同時,我們也會探討如何使用程式碼檢查工具,例如 golint,來提升程式碼品質,並利用 Nancy 等工具進行安全性掃描,降低潛在風險。最後,我們將介紹如何運用 Jenkins 的平行測試功能,加速測試流程,縮短整體建置時間。

使用 Jenkins 執行自動化測試於微服務架構

在前一章中,我們學習瞭如何設定多分支Pipeline任務(multibranch pipeline jobs)於容器化的微服務,並且透過 Webhook 持續觸發 Jenkins 任務。本章將聚焦於在持續整合(CI)Pipeline中執行自動化測試。圖 8.1 總結了目前的 CI 工作流程階段。

本章涵蓋內容

  • 為根據 Python、Go、Node.js 和 Angular 的服務實作 CI Pipeline
  • 使用 Headless Chrome 執行預整合測試和自動化 UI 測試
  • 在 Jenkins Pipeline中執行 SonarQube 靜態程式碼分析
  • 在 Docker 容器內執行單元測試並發布程式碼覆寫率報告
  • 在 Jenkins Pipeline中整合相依性檢查並注入安全機制於 DevOps

圖 8.1 本章涵蓋的測試階段

測試自動化被視為敏捷開發的根本。若希望快速發布產品(甚至每日發布),並保持合理的品質,就必須轉向自動化測試。另一方面,忽視測試可能會導致客戶不滿和產品延遲。然而,自動化測試過程比自動化建置、發布和佈署過程更具挑戰性。自動化應用程式中使用的幾乎所有測試案例通常需要耗費大量精力。這是一種隨著時間而成熟的活動。並非總是能夠自動化所有測試,但目標是盡可能自動化。

圖 8.2 目標 CI Pipeline

在繼續實作 CI Pipeline之前,先簡要回顧一下與 Jenkins 整合的 Web 分散式應用程式架構:它根據微服務架構,並拆分為以不同程式語言和框架撰寫的元件/服務。圖 8.3 說明瞭此架構。

在 Docker 容器內執行單元測試

單元測試是盡早識別問題的前線努力。測試需要小而快速,以提高效率。

Python 單元測試範例

movies-loader 服務以 Python 編寫。為了定義單元測試,我們將使用 unittest 框架(隨 Python 安裝附帶)。首先,我們匯入 unittest 模組,它提供豐富的方法來建構和執行測試。以下清單 test_main.py 示範了一個簡短的單元測試,用於測試 JSON 載入和解析機制。

import unittest
import json

class TestJSONLoaderMethods(unittest.TestCase):
    movies = []

    @classmethod
    def setUpClass(cls):
        with open('movies.json') as json_file:
            cls.movies = json.load(json_file)

    def test_rank(self):
        self.assertEqual(self.movies[0]['rank'], '1')

    def test_title(self):
        self.assertEqual(self.movies[0]['title'], 'The Shawshank Redemption')

    def test_id(self):
        self.assertEqual(self.movies[0]['id'], 'tt0111161')

if __name__ == '__main__':
    unittest.main()

Dockerfile.test

為了在 Docker 容器內執行測試,我們建立一個名為 Dockerfile.test 的檔案,內容如下:

FROM python:3.7.3
WORKDIR /app
COPY test_main.py .
COPY movies.json .

這個 Dockerfile 從官方的 Python 3.7.3 映像檔建置,設定了一個名為 app 的工作目錄,並將測試檔案複製到工作目錄。

更新 Jenkinsfile

接下來,更新 Jenkinsfile 以新增一個名為「Unit Test」的階段,如下所示:

def imageName = 'mlabouardy/movies-loader'

node('workers') {
    stage('Checkout') {
        checkout scm
    }
    stage('Unit Test') {
        // 建立 Docker 映像檔並執行單元測試
        docker.build("${imageName}-test", "-f Dockerfile.test .")
        docker.image("${imageName}-test").run("--rm")
    }
}

內容解密:

  1. Docker 建置與執行:在「Unit Test」階段,我們使用 Docker 建置一個名為 ${imageName}-test 的映像檔,根據 Dockerfile.test
  2. 單元測試執行:建置完成後,我們從該映像檔啟動一個容器來執行單元測試。
  3. --rm 引數:容器執行完畢後自動刪除,避免佔用資源。

圖 8.3 Watchlist 微服務架構

本章節詳細介紹瞭如何在 Jenkins 中設定自動化測試流程,包括單元測試的撰寫、Dockerfile 的建立以及 Jenkinsfile 的更新,以實作在 Docker 容器內執行單元測試。接下來的章節將進一步探討其他型別的測試,如整合測試、UI 測試及靜態程式碼分析等。

在 Docker 容器中執行單元測試

在 Jenkins 中整合單元測試是一個重要的步驟,以確保程式碼的正確性和穩定性。以下將介紹如何使用 Docker 容器執行單元測試。

建立單元測試階段

首先,在 Jenkinsfile 中新增一個階段(stage)來執行單元測試。這個階段將使用 Docker 建置一個測試映像,並執行測試。

stage('Unit Tests'){
    sh "docker build -t ${imageName}-test -f Dockerfile.test ."
    sh "docker run --rm ${imageName}-test"
}

在這個範例中,docker build 命令用於建立一個名為 ${imageName}-test 的 Docker 映像,而 docker run 命令則用於執行這個映像中的測試。--rm 旗標用於在測試完成後自動刪除容器。

使用 Docker DSL

除了使用 shell 命令外,也可以使用 Docker DSL 來執行測試。Docker DSL 提供了一個更高層級的抽象,使得程式碼更易讀和維護。

stage('Unit Tests'){
    def imageTest = docker.build("${imageName}-test", "-f Dockerfile.test .")
    imageTest.inside{
        sh 'python test_main.py'
    }
}

在這個範例中,docker.build() 方法用於建立一個 Docker 映像,而 inside() 方法則用於在這個映像中執行測試。

內容解密:

  1. docker.build() 方法會建立一個新的 Docker 映像,並傳回一個可以代表這個映像的物件。
  2. inside() 方法會在這個映像中啟動一個新的容器,並在其中執行指定的命令。
  3. sh 'python test_main.py' 命令會在容器中執行 test_main.py 指令碼,進行單元測試。

產生 JUnit 測試報告

為了讓測試結果更容易被理解,可以使用 JUnit 測試報告。JUnit 是一個廣泛被使用的測試框架,而 Jenkins 也對 JUnit 測試報告提供了良好的支援。

首先,需要更新 test_main.py 指令碼,以便產生 JUnit 格式的測試報告。

import xmlrunner
...
if __name__ == '__main__':
    runner = xmlrunner.XMLTestRunner(output='reports')
    unittest.main(testRunner=runner)

接著,在 Jenkinsfile 中新增一個步驟,以便將測試報告上傳到 Jenkins。

stage('Unit Tests'){
    def imageTest = docker.build("${imageName}-test", "-f Dockerfile.test .")
    sh "docker run --rm -v $PWD/reports:/app/reports ${imageName}-test"
    junit "$PWD/reports/*.xml"
}

內容解密:

  1. sh "docker run --rm -v $PWD/reports:/app/reports ${imageName}-test" 命令會啟動一個新的容器,並將當前目錄下的 reports 目錄掛載到容器中的 /app/reports 目錄。
  2. junit "$PWD/reports/*.xml" 命令會將 reports 目錄下的 JUnit 測試報告上傳到 Jenkins。

自動化程式碼檢查

除了單元測試外,程式碼檢查也是確保程式碼品質的重要步驟。以下將介紹如何使用 Jenkins 自動化程式碼檢查。

使用 Go Linter

首先,需要在 Dockerfile.test 中安裝 Go linter。

FROM golang:1.13.4
WORKDIR /go/src/github.com/mlabouardy/movies-loader
ENV GOCACHE /tmp
WORKDIR /go/src/github/mlabouardy/movies-parser
RUN go get -u golang.org/x/lint/golint
COPY . .
RUN go get -v

接著,在 Jenkinsfile 中新增一個階段,以便執行 Go linter。

stage('Quality Tests'){
    def imageTest = docker.build("${imageName}-test", "-f Dockerfile.test .")
    imageTest.inside{
        sh 'golint'
    }
}

內容解密:

  1. sh 'golint' 命令會在容器中執行 Go linter,檢查程式碼的品質。
  2. 如果需要讓 Jenkins 在發現程式碼問題時失敗,可以在 golint 命令中新增 -set_exit_status 旗標。
stage('Quality Tests'){
    def imageTest = docker.build("${imageName}-test", "-f Dockerfile.test .")
    imageTest.inside{
        sh 'golint -set_exit_status'
    }
}

此圖示顯示了程式碼檢查的流程:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Jenkins 自動化測試微服務架構

package "Docker 架構" {
    actor "開發者" as dev

    package "Docker Engine" {
        component [Docker Daemon] as daemon
        component [Docker CLI] as cli
        component [REST API] as api
    }

    package "容器運行時" {
        component [containerd] as containerd
        component [runc] as runc
    }

    package "儲存" {
        database [Images] as images
        database [Volumes] as volumes
        database [Networks] as networks
    }

    cloud "Registry" as registry
}

dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置

@enduml

圖表翻譯: 此圖表呈現了程式碼檢查的流程。首先,建置一個 Docker 映像,接著在這個映像中執行 Go linter。如果 Go linter 發現了程式碼問題,則會失敗並回報問題;否則,程式碼檢查成功。

自動化測試與 Jenkins 的整合應用

在現代軟體開發流程中,自動化測試是確保程式碼品質的重要環節。透過 Jenkins 這類別持續整合/持續佈署(CI/CD)工具,可以有效地執行自動化測試並產生相關報告。本篇文章將探討如何利用 Jenkins 執行 Go 語言專案的自動化測試,包括單元測試、程式碼覆寫率報告以及安全性測試。

單元測試的基礎架構

在 Go 語言中,單元測試是透過內建的 testing 套件來實作的。一個基本的單元測試函式需要接受 *testing.T 作為引數,並使用 t.Error() 方法來標示測試失敗。以下是一個簡單的範例:

func TestParseMovie(t *testing.T) {
    expectedMovie := Movie{
        Title:        "John Wick (2014)",
        ReleaseDate:  "24 October 2014 (USA)",
        Description:  "An ex-hit-man comes ...",
    }
    currentMovie, err := ParseMovie(HTML)
    if expectedMovie.Title != currentMovie.Title {
        t.Errorf("returned wrong title: got %v want %v", currentMovie.Title, expectedMovie.Title)
    }
}

內容解密:

  1. 測試函式的定義:測試函式以 Test 開頭,後面接著首字母大寫的函式名稱,如 TestParseMovie
  2. 使用 *testing.T:透過 t.Errorf 方法報告錯誤,指出預期值與實際值之間的差異。
  3. ParseMovie 函式的測試:此範例主要測試 ParseMovie 函式是否正確解析 HTML 內容並產生預期的 Movie 結構。

程式碼覆寫率報告的生成

為了評估測試的完整性,Go 語言提供了內建的程式碼覆寫率分析工具。透過以下命令,可以生成 HTML 格式的覆寫率報告:

go test -coverprofile=cover/cover.cov
go tool cover -html=cover/coverage.cov -o coverage.html

內容解密:

  1. go test -coverprofile:執行測試並生成覆寫率檔案 cover.cov
  2. go tool cover -html:將覆寫率檔案轉換為 HTML 報告,顯示每一行程式碼的覆寫情況。
  3. Jenkins 整合:可將此命令納入 CI 工作流程,在測試階段後生成並展示覆寫率報告。

在 CI 管道中注入安全性測試

為了確保專案依賴元件的安全性,可以使用 Nancy 這類別工具掃描已知漏洞。以下是如何在 Jenkinsfile 中新增安全性測試階段:

stage('Security Tests'){
    imageTest.inside('-u root:root'){
        sh 'nancy /go/src/github/mlabouardy/movies-parser/Gopkg.lock'
    }
}

內容解密:

  1. Nancy 的安裝與組態:在 Dockerfile.test 中安裝 Nancy 並設定執行路徑。
  2. Gopkg.lock 的掃描:Nancy 使用 Gopkg.lock 檔案檢查專案依賴的 Go 套件是否存在已知漏洞。
  3. 安全性報告:若發現漏洞,Nancy 將以非零程式碼離開,使建置流程失敗。

平行測試的執行

隨著測試數量的增加,執行時間可能成為瓶頸。Jenkins 提供了 parallel DSL 步驟來平行執行測試階段,以縮短整體建置時間。

parallel {
    stage('Unit Tests') { 
        // 執行單元測試
    }
    stage('Other Tests') { 
        // 執行其他型別的測試
    }
}

圖表翻譯:

此圖示展示瞭如何在 Jenkins 中平行執行多個測試階段,以提高效率。