TestContainer使用者使用經驗

banq發表於2024-02-28


TestContainer是整合測試的遊戲規則改變者,它們具有特定於語言的 docker api,這使得啟動容器並驗證它們是否已完全初始化並準備好接受連線變得很簡單。

不再需要模擬或複雜的環境配置。將測試依賴項定義為程式碼,然後只需執行測試,容器就會被建立然後刪除。

不需要單元測試,用容器執行整合測試?

  • 至少在 Java 世界中,"單元測試 "一詞經常被 "在 JUnit 中做的事情 "所混淆,JUnit 同時執行 "純 "單元測試和專案級整合測試,即啟動應用程式上下文(如 Spring)並針對真實 REST 端點等進行測試。
  • 雖然單元測試比(專案級)整合測試更便宜、更快捷,但在很多情況下,單元測試提供的結果和可信度也不如整合測試,因為很多執行時方面(序列化、HTTP 響應、資料庫響應等)都無法直接模擬。
  • 使用實際資料庫/Elasticsearch/Redis/Varnish 等的整合測試比傳統的單元測試更有價值。

關於 "測試獎盃"(The Testing Trophy)而非 "測試金字塔"(Testing Pyramid)的討論甚囂塵上。整合測試的速度較慢,但也僅僅是慢了那麼一點點,所以往往是值得的。是否值得在很大程度上取決於測試的內容。
  • 如果是 CRUD API:使用整合測試。
  • 如果是演算法或字串操作等:使用單元測試。


最佳實踐
設定 CI,以便它進行 lint、構建、單元測試,然後整合測試(使用測試容器)
https://github.com/turbolytics/latte/blob/main/.github/workf...

name: CI
on: [push]
jobs:
  ci:
    name: CI
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go 1.21.x
        uses: actions/setup-go@v4
        with:
          go-version: 1.21.x
        id: go

      - name: Build
        run: go build -v ./...

      - name: Run unit tests
        run: make test-unit

      - name: Run integration tests
        run: make test-integration

它們的語言繫結為常見資料庫操作提供了很好的輔助函式(例如從容器使用者生成連線 uri)
https://github.com/turbolytics/latte/blob/main/internal/sour...

package mongodb

import (
    <font>"context"
    
"fmt"
    
"github.com/stretchr/testify/assert"
    
"github.com/testcontainers/testcontainers-go"
    
"github.com/testcontainers/testcontainers-go/modules/mongodb"
    
"github.com/turbolytics/latte/internal/metric"
    
"go.mongodb.org/mongo-driver/mongo"
    
"go.mongodb.org/mongo-driver/mongo/options"
    
"testing"
    
"time"
)

func TestIntegration_Mongo_Source(t *testing.T) {
    if testing.Short() {
        t.Skip(
"skipping integration test")
    }

    ctx := context.Background()
    mongodbContainer, err := mongodb.RunContainer(
        ctx,
        testcontainers.WithImage(
"mongo:6"),
    )

    
// Clean up the container<i>
    defer func() {
        if err := mongodbContainer.Terminate(ctx); err != nil {
            assert.NoError(t, err)
        }
    }()

    endpoint, err := mongodbContainer.ConnectionString(ctx)
    if err != nil {
        assert.NoError(t, err)
    }

    mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(endpoint))
    if err != nil {
        assert.NoError(t, err)
    }

    type user struct {
        Account    string
        SignupTime time.Time
    }

    coll := mongoClient.Database(
"test").Collection("users")
    docs := []interface{}{
        user{Account:
"amazon", SignupTime: time.Now().UTC()},
        user{Account:
"amazon", SignupTime: time.Now().UTC()},
        user{Account:
"google", SignupTime: time.Now().UTC()},
    }
    result, err := coll.InsertMany(context.TODO(), docs)
    fmt.Printf(
"Documents inserted: %v\n", len(result.InsertedIDs))

    mSource, err := NewFromGenericConfig(
        ctx,
        map[string]any{
            
"uri":        endpoint,
            
"database":   "test",
            
"collection": "users",
            
"agg": `
  [
    { 
      
"$group": { "_id": "$account", "value": { "$count": {} } } 
    },
    {
"$sort" : { "_id" : 1 } },
    { 
      
"$addFields": { 
        
"account": {  "$toString": "$_id"
      } 
    }, 
    { 
      
"$project": { "_id": 0 }
    }
  ]
`,
        },
        false,
    )

    assert.NoError(t, err)
    rs, err := mSource.Source(ctx)
    assert.NoError(t, err)

    ms := rs.(*metric.Metrics)

    for _, m := range ms.Metrics {
        m.Timestamp = time.Time{}
        m.UUID =
""
    }

    assert.Equal(t, []*metric.Metric{
        {
            Value: 2,
            Tags: map[string]string{
                
"account": "amazon",
            },
        },
        {
            Value: 1,
            Tags: map[string]string{
                
"account": "google",
            },
        },
    }, ms.Metrics)

}


可以在任何地方都使用它們:)

網友討論:
1、作為 testcontainers 的使用者,我可以告訴你它們非常強大但簡單。

事實上,它們所做的只是為您的語言提供抽象,但這對於單元/整合測試非常有用。

在我的工作中,我們有許多 Java 和 Python 微服務,所有這些微服務都使用 testcontainers 來設定本地環境或整合測試。我發現與 localstack 的整合以及以程式設計方式設定它而無需與撰寫檔案進行鬥爭的能力非常有用。

2、我們使用 kubedock在 kubernetes 叢集中執行測試容器。只要您只拉取影像,而不是構建或載入它們(kubedock 明確不支援),它就可以很好地工作。

相關文章