微服務架構的測試策略

1.1 引言

在微服務架構下,單個微服務的測試確實變得更加簡單,但是與此同時,模塊(微服務)與模塊(微服務)之間的測試變得困難起來。

這是因爲所有的微服務都可能處於同一局域網的不同服務器上,想要對他們進行測試並不是一件容易的事情。

進程間通信是微服務架構的核心,基於微服務架構的應用程序是一個分佈式系統。

1.2 微服務架構下各個微服務怎麼測?

在開始回答這個問題之前,我們先來了解下什麼是測試?

1.2.1 什麼是測試?

測試用例是用於特定目標的一組測試輸入,執行條件和預期結果,例如執行特定的程序路徑或驗證是否符合特定要求。——維基百科

也就是說,測試就是輸入一些測試數據,程序執行後得到我們期望的結果,如果一致,測試通過,反之有問題。

1.2.2 測試的不同類型?

知道什麼是測試之後,我們再來了解下都有哪些測試類型。

因爲我們必須爲我們的應用程序編寫不同類型的測試,來確保應用程序有效。

根據測試範圍的不同,大致可以將測試分爲如下四類:

  • 單元測試:在java這樣面嚮對象語言中,測試的目標就是類,一般由開發人員完成。
  • 集成測試:驗證服務是否可以與基礎設置服務如數據庫,消息中間件或其他應用程序服務進行交互。
  • 組件測試:單個微服務(模塊)的驗收測試
  • 端到端測試:整個應用程序的驗收測試(各個微服務之間的調用測試)

這四種測試的難易程度不盡相同,一個極端是單元測試,執行起來快,易於編寫且可靠。

另一個極端是針對整個應用程序的端到端測試,也就是微服務到微服務之間的測試。

下面這張測試金字塔圖挺不錯的,很好總結了各個測試類型的難易程度。
在這裏插入圖片描述

PS:另一個參考價值在於,測試的過程中, 多點單元測試,集成測試次數,相對少點組件測試和端到端測試次數。

1.2.2.1 單元測試的建議

JUnit 是一種流行的Java 測試框架,每個測試都有一個測試方法實現。

一個測試類最好有一個在所有測試類執行前的初始化方法,以及在最後運行的清理方法。

  • 在JUnit5 有了很多新的註解可以幫助我們高效完成單元測試需求
    • 比如我們可以使用 @BeforeAll可以在當前類的所有方法之前前執行方法,
    • @AfterAll在所有測試方法執行後執行方法,
    • 甚至可以使用@TestMethodOrder註解指定測試方法的執行前後順序。
  • 點擊查看更多註解

如果我們想只是測試一個Controller 而把整個應用程序所有組件都啓動起來是不切實際的。

因此,針對單元測試的一個建議是儘可能使用測試替身。

測試替身一般有兩種,一種是使用Mock對象,另外一種是Stub對象。

1.2.2.1.1 Mock對象

關於Mock 對象,很多人應該比較熟悉,MockEnvironment,MockMvc,

這裏貼一個例子:

class MyWebTests {

    MockMvc mockMvc;

    @BeforeEach
    void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
    }

    @Test
    void getAccount() throws Exception {
        this.mockMvc.perform(get("/accounts/1")
                .accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().contentType("application/json"))
            .andExpect(jsonPath("$.name").value("Lee"));
    }
}
1.2.2.1.2 Stub 對象

另外一種Stub (存根)對象,這個是神馬東東呢?

Stub 江湖俗稱佔坑對象,稍後詳解。

爲屏蔽客戶調用遠程主機上的對象,必須提供某種方式來模擬本地對象,這種本地對象稱爲存根(stub),存根負責接收本地方法調用,並將它們委派給各自的具體實現對象

1.2.2.2 端到端的測試建議

假設我們有一個包含多個微服務的系統,如下圖所示:
在這裏插入圖片描述
如果我們要測試上圖中這個應用程序,看看它是否可以與其他服務通信,我們可以執行以下兩項操作之一:

  • 部署所有微服務並執行端到端測試。
  • 在單元和集成測試中模擬其他微服務。

這兩種方法都有優點,也有很多缺點。

第一種方式:部署所有微服務並執行端到端測試

優點:

  • 模擬生產環境
  • 測試服務之間的真實通信

缺點:

  • 爲了測試一個微服務,我們必須部署六個微服務,幾個數據庫以及其他項目
  • 測試運行的環境被鎖定爲單個測試套件(在此期間,其他任何人都無法運行測試)
  • 需要很長時間才能運行
  • 反饋在此過程中非常慢
  • 很難調試

第二種方式:在單元和集成測試中模擬其他微服務

優點:

  • 他們提供了非常快速的反饋。
  • 他們沒有基礎架構要求。

缺點:

  • 服務的實現者創建的存根可能與現實無關。
  • 您可以通過測試並通過失敗的生產。

因此最好避免像這樣的端到端測試。

那麼有沒有什麼更好的測試方法呢?

答案是肯定的,那就是使用契約測試。

1.2.2.2.1 什麼是契約測試?

那麼什麼是契約測試呢?
在這裏插入圖片描述

  • 契約測試通常使用樣例測試,消費者和提供者之間的交互由一組樣例定義,稱爲契約。
  • 每個契約都包含在一次交互期間交換的樣例消息。

契約測試提供了一種新的測試方式 - 基於接口,也就是說每個微服務都提供自己的測試接口以及請求的測試數據,期待的測試結果。

例如,REST API 的契約包含示例HTTP 請求和響應。

1.2.2.2.2 契約測試的設計思想

契約測試的主要設計思想是爲我們提供非常快速的反饋,而無需建立整個微服務。 如果我們使用stub,則僅需要應用程序直接使用的應用程序。

可以把契約理解成接口,不過這個接口不是傳統意義上的接口。它是一段groovy或yaml代碼,消費者和生產者在這段代碼中約定了request 什麼就可以response什麼。一方面生產者可以自己去驗證是否實現了契約,即真實開發完成後檢查能否實現request什麼,就返回什麼的契約。消費者測試不會真的等生產者啓動服務,而是使用框架給生產者根據契約生成一個模擬的stub(mock)文件,它可以提供同樣的模擬服務,比如request什麼,response什麼。
在這裏插入圖片描述

1.2.2.2.3 如何實現契約測試?

兩個比較流行的企業級契約測試框架是Spring Cloud Contract 和支持多種語言的Pact系列框架。

默認情況下,Spring Cloud Contract與Wiremock集成爲HTTP服務器Stub(存根)。

這裏貼一段示例體會下,更多請移步官方文檔參考學習。

package contracts

org.springframework.cloud.contract.spec.Contract.make {
    request { // (1)
        method 'PUT' // (2)
        url '/fraudcheck' // (3)
        body([ // (4)
               "client.id": $(regex('[0-9]{10}')),
               loanAmount : 99999
        ])
        headers { // (5)
            contentType('application/json')
        }
    }
    response { // (6)
        status OK() // (7)
        body([ // (8)
               fraudCheckStatus  : "FRAUD",
               "rejection.reason": "Amount too high"
        ])
        headers { // (9)
            contentType('application/json')
        }
    }
}

1.2.3 部署流水線實現自動化測試

其實很多企業都是手動測試,缺乏自動化測試的原因主要是文化,比如“測試是QA的工作,開發人員的時間不應該花在測試上”,還有一部分原因是一些開發人員並不十分熟悉如何寫好測試用例。

但是在微服務架構下最好的實踐是集成自動化測試。
在這裏插入圖片描述
上圖所示的部署流水線包括如下階段:

  • 提交前的測試階段:執行單元測試,這是由開發人員在提交代碼變更之前執行的。
  • 提交測試階段:編譯服務,執行單元測試,並執行靜態代碼分析。
  • 集成測試框架:執行集成測試
  • 組件測試階段:執行服務的組件測試。
  • 部署階段:將服務部署到生產環境中。

當開發人員提交代碼更改時,持續集成服務器(比如Jenkins)就會運行提交測試。

處理提交前的測試不是自動化的,其他部分基本上都是自動化測試。

但是值得注意的是,有些情況仍然需要手動操作,比如將項目打包發佈到生產環境的時候。

在這種情況下,最好只有測試人員單擊一個按鈕表明該階段通過時纔會進入下一階段。

1.3 微服務架構下的測試總結

單體架構下應用開發的測試通常都是在開發完成後執行,而且大多都是手工測試,但是當到了微服務架構這種方式不再適用。

  • 一方面原因在於手動測試效率極地,微服務與微服務之間跨進程的測試,手動測試費時費力。
  • 另一方面等到交付流程時才進行測試時候爲時已晚,會延長項目交付日期。

總結建議如下:

  • 自動化測試是快速,安全交付軟件的重要基礎,由於微服務架構固有的複雜性,必須實現自動化測試。
  • 簡單和加快測試的方式是使用測試替身,比如Mock 對象或Spring Cloud Contract.
  • 根據測試金字塔參考應該儘可能減少端到端測試的數量,因爲寫入耗時,脆弱,且耗時。

1.4 參考資料

本篇完~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章