e2e測試,實現(xiàn)自動化聯(lián)調(diào)

概念

e2e測試,a.k.a.“自動化聯(lián)調(diào)”,模擬最終用戶的操作、看效果。
不需要mock/fake環(huán)境,依賴多個系統(tǒng)的真實環(huán)境。

優(yōu)點:

  • 相比于UT、集成測試:
    • 更貼近真實場景,更能發(fā)現(xiàn)問題
    • 寫測試用例的成本更低(因為不需要mock/fake環(huán)境)
  • 相比于人工聯(lián)調(diào):
    • 省去聯(lián)調(diào)時大量的溝通協(xié)作成本、人肉操作成本
    • 全鏈路任何一個系統(tǒng)發(fā)生變更后(代碼變更或配置變更),可以自動化回歸測試
  • 方便對環(huán)境做故障注入,看系統(tǒng)在特定故障時的表現(xiàn)

更詳細(xì)的概念介紹:
https://circleci.com/blog/what-is-end-to-end-testing/

Design choices

怎么觸發(fā)、怎么setup環(huán)境

A. CI 里觸發(fā),原地創(chuàng)建環(huán)境、跑測試

創(chuàng)建環(huán)境可以基于k8s:

  • 先調(diào)k8s接口,刪除原先的資源
  • 然后再通過kubectl(或者調(diào)k8s接口)創(chuàng)建(聯(lián)調(diào)需要用到的)環(huán)境。

沒有k8s的話也可以基于 docker或者 docker-compose 創(chuàng)建環(huán)境。

B. merge后持續(xù)部署,部署完成后自動觸發(fā)E2E測試

觸發(fā)后需要保證互斥執(zhí)行,避免并發(fā)跑測試。

pros:

  • 寫test case不需要mock不需要fake,省去setup環(huán)境的工作

cons:

  • 條件可能比較苛刻。比如,如果e2e測試依賴多個系統(tǒng),需要:
    • 多個系統(tǒng)都設(shè)置持續(xù)部署
    • 并發(fā)部署時,有個編排系統(tǒng),保證互斥、避免重復(fù)執(zhí)行。

C. 定時巡檢

巡檢腳本可以臨時找機(jī)器做測試,也可以使用固定的機(jī)器做測試
pros:

  • 寫test case不需要mock不需要fake,省去setup環(huán)境的工作
  • 和部署方案解耦,條件沒B方案那么苛刻,成本低

多分支開發(fā),但測試環(huán)境只有一套,怎么辦?

A. CI里創(chuàng)建環(huán)境,讓測試環(huán)境有多套
B. 針對指定分支(例如develop) 設(shè)置持續(xù)部署。要求所有分支上線前都得合并進(jìn)這個分支,合并后觸發(fā)自動部署。

案例分析

K8s 如何做e2e測試

怎么觸發(fā)e2e測試

CI 里觸發(fā),原地創(chuàng)建 K8s集群,然后跑測試

觸發(fā)e2e測試后,會發(fā)生什么

  1. 工具鏈會創(chuàng)建并啟動一個測試用的K8s集群(或者使用現(xiàn)有集群)

  2. 跑 E2E 測試用例。

這些測試用例本質(zhì)上是K8s的客戶端,它們會調(diào) K8s的接口、使用K8s、斷言預(yù)期效果。

  1. 清理掉臨時的K8S集群

詳見
https://blog.gmem.cc/kubernetes-e2e-test

例子

測試Pod能讀取secret

  1. 準(zhǔn)備好yaml,調(diào)用Kubectl來創(chuàng)建Secret、創(chuàng)建會讀取此Secret并打印的Pod
  2. 等待Pod退出
  3. 檢查Pod日志,斷言出現(xiàn)了關(guān)鍵詞

// 聲明一個ginkgo.Describe塊,自動添加[k8s.io] 標(biāo)簽
var _ = framework.KubeDescribe("[Feature:Example]", func() {
    //省略……
    
    framework.KubeDescribe("Secret", func() {
        // 第二個Spec,測試Pod能讀取一個保密字典
        ginkgo.It("should create a pod that reads a secret", func() {
            test := "test/fixtures/doc-yaml/user-guide/secrets"
            secretYaml := readFile(test, "secret.yaml")
            podYaml := readFile(test, "secret-pod.yaml.in")
 
            nsFlag := fmt.Sprintf("--namespace=%v", ns)
            podName := "secret-test-pod"
 
            ginkgo.By("creating secret and pod")
            // 創(chuàng)建一個Secret,以及會讀取此Secret并打印的Pod
            framework.RunKubectlOrDieInput(secretYaml, "create", "-f", "-", nsFlag)
            framework.RunKubectlOrDieInput(podYaml, "create", "-f", "-", nsFlag)
            // 等待Pod退出
            err := e2epod.WaitForPodNoLongerRunningInNamespace(c, podName, ns)
            framework.ExpectNoError(err)
 
            ginkgo.By("checking if secret was read correctly")
            // 檢查Pod日志
            _, err = framework.LookForStringInLog(ns, "secret-test-pod", "test-container", "value-1", serverStartTimeout)
            framework.ExpectNoError(err)
        })
    })
}

測試健康檢查失敗的Pod能否自動重啟

  1. 準(zhǔn)備好yaml,調(diào)用Kubectl來創(chuàng)建資源
  2. 輪詢調(diào)接口,查該pod的重啟次數(shù)
  3. 如果在一定時間內(nèi)沒滿足預(yù)期,就報錯

和其他系統(tǒng)集成(聯(lián)調(diào))的案例

  • K8s + Helm

https://github.com/kubernetes-sigs/e2e-framework/tree/main/examples/third_party_integration/helm

  • K8s + Flux

https://github.com/kubernetes-sigs/e2e-framework/tree/main/examples/third_party_integration/flux

怎么斷言別的Pod內(nèi)發(fā)生了什么

寫測試用例時,可以使用社區(qū)的測試框架,框架提供了實用的Utils庫,例如,提供了用于斷言“日志中出現(xiàn)關(guān)鍵詞”的函數(shù)

怎么模擬各種異常場景

  • 框架有提供一些工具函數(shù),例如:
image.png
  • 框架提供NodeKiller,負(fù)責(zé)周期性的模擬節(jié)點失敗
if framework.TestContext.NodeKiller.Enabled {
    nodeKiller := framework.NewNodeKiller(framework.TestContext.NodeKiller, c, framework.TestContext.Provider)
    // NodeKiller負(fù)責(zé)周期性的模擬節(jié)點失敗
    go nodeKiller.Run(framework.TestContext.NodeKiller.NodeKillerStopCh)
}
  • 自己操作pod,在pod里執(zhí)行命令
  • 在自己創(chuàng)建的pod里寫程序、制造故障

代理軟件如何做e2e測試

Dapr

image.png

怎么觸發(fā)e2e測試

提PR后,可以在 CI里觸發(fā)(需要 maintainer or approver 在PR里評論 /ok-to-test),會用預(yù)先準(zhǔn)備好的 AKS 集群跑測試。

也可以用自己的 K8s 集群,手動觸發(fā)、跑測試。

觸發(fā)e2e測試后,會發(fā)生什么

  1. 調(diào) K8s,部署dapr代理的 redis/kafka/mongodb等系統(tǒng)
  2. 部署dapr:構(gòu)建,打包成鏡像,發(fā)布到鏡像倉庫,調(diào)K8s部署鏡像
  3. 配置dapr。Register the default component configurations for testing
  4. 跑e2e測試:構(gòu)建e2e app,打包成鏡像,發(fā)布到鏡像倉庫,調(diào)K8s部署,運行e2e app

https://github.com/dapr/dapr/blob/master/tests/docs/running-e2e-test.md#user-content-run-e2e-tests-in-local-dev-environment

e2e測試的例子

https://github.com/dapr/dapr/blob/master/tests/docs/writing-e2e-test.md

State測試

以 state 的測試為例:

image.png
Test drive

包含真正的test case,本質(zhì)是客戶端,斷言發(fā)包收包結(jié)果。
比如“寫-讀-刪-讀”測試,斷言每一步收到的response:

image.png

每一步讀寫操作其實是發(fā)http請求調(diào) test app,由 test app 調(diào)dapr
https://github.com/dapr/dapr/blob/master/tests/e2e/stateapp/stateapp_test.go#L396

Test app

收到 Test driver 的命令,給dapr 發(fā)請求
https://github.com/dapr/dapr/blob/master/tests/apps/stateapp/app.go#L464

image.png
RPC測試
image.png
Test driver

https://github.com/dapr/dapr/blob/master/tests/e2e/service_invocation/service_invocation_test.go#L79
https://github.com/dapr/dapr/blob/master/tests/e2e/service_invocation/service_invocation_test.go#L229
[圖片上傳失敗...(image-acb490-1693271805183)]

Test app

https://github.com/dapr/dapr/blob/master/tests/apps/service_invocation/app.go#L174

https://github.com/dapr/dapr/blob/master/tests/apps/service_invocation/app.go#L299

咋斷言upstream收到了某個包?

看起來沒斷言upstream 收到的包,只是斷言 client 收到的最終response

Layotto

https://mosn.io/layotto/#/zh/development/test-quickstart

CI 里原地創(chuàng)建環(huán)境。沒依賴 k8s,基于 docker或者 docker-compose 創(chuàng)建環(huán)境。
運行測試用例,本質(zhì)是客戶端,斷言發(fā)包收包結(jié)果。

有UI的APP/網(wǎng)站如何做e2e測試

支付寶首頁助理

定時巡檢測試:

  • 下發(fā)假數(shù)據(jù)
  • 調(diào)前端接口
  • assert接口返回的數(shù)據(jù)內(nèi)容

以上測試直接在線上/預(yù)發(fā)環(huán)境運行,用于變更后測試,及時發(fā)現(xiàn)變更引入的問題,包括全鏈路任何一個系統(tǒng)的配置變更、代碼變更

高級功能

如何在pod內(nèi)實現(xiàn)故障注入

方案:測試用例在目標(biāo)pod 中運行,因此測試用例有權(quán)限原地制造故障
缺點:只能在本pod內(nèi)制造故障,沒法跨pod/跨節(jié)點故障注入

如何跨pod操作(例如跨pod故障注入、跨pod查日志)

A. 給所在節(jié)點添加 DaemonSet,DaemonSet 想辦法侵入其他pod的namespace、干擾其他pod

參考 chaos mesh

B. 遠(yuǎn)程登錄指定pod,然后執(zhí)行shell命令

缺點:遠(yuǎn)程登錄有權(quán)限問題

C. 基于K8s API 讓其他pod執(zhí)行shell

D. pod內(nèi)安插間諜(后門)

Q: 如何植入后門?

方案A. 可以在給相關(guān) pod 內(nèi)添加守護(hù)進(jìn)程/sidecar容器;
好處是不侵入業(yè)務(wù)進(jìn)程
缺點:
新起一個進(jìn)程有一定的開發(fā)成本;

方案B. 直接在目標(biāo)pod的業(yè)務(wù)進(jìn)程里實現(xiàn)后門功能(可以通過引入一個庫來自動實現(xiàn),減少侵入性);

Q: 后門如何對外暴露接口?

方案A. 每個服務(wù)開放debug接口 (RPC/IPC 接口),用于跨pod操作(例如故障注入,讀取日志等);

方案B. 不開接口,test app依賴有watch機(jī)制的外部存儲,一個節(jié)點寫入命令,另一個節(jié)點watch、執(zhí)行命令
難點是怎么保證只執(zhí)行一次

Q: 編程界面?

方案A. 參考k8s,設(shè)計utils 庫,用于在指定的pod執(zhí)行操作:
[圖片上傳失敗...(image-56c068-1693271805183)]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容