在GitLab CI中对Cloud Datastore进行测试

太长没仔细看

    • golang でのテストのお話

 

    • Cloud Datastore は gRPC なので http.Client の変更でモック化する httpmock とかが使えない

 

    Cloud Datastore Emulator を GitLab CI の services として起動しておけばテストできる

事件的起源

如果有以下的程式,試著使用 httpmock 方法將對 Cloud Datastore 的請求進行模擬並撰寫測試程式碼,但在建立 datastore.Client 時出現錯誤。

    main.go
package main

import (
    "context"
    "log"
    "os"

    // golang.org
    "golang.org/x/oauth2/google"

    // google.golang.org
    "google.golang.org/api/option"

    // cloud.google.com
    "cloud.google.com/go/datastore"
)

func main() {
    ctx := context.Background()

    // 1. Get google application credentials.
    var credOption option.ClientOption
    credentials, err := google.FindDefaultCredentials(ctx)
        if err != nil {
        log.Fatal(err)
    }
    if credentials.JSON != nil {
        credOption = option.WithCredentialsJSON(credentials.JSON)
        } else {
                credOption = option.WithTokenSource(credentials.TokenSource)
        }

    // 2. Put value.
    err = PutValue("xxxxxxxxxxx", "example", credOption)
    if err != nil {
        log.Fatal(err)
    }
}

type Item struct{
    Key *datastore.Key `datastore:"__key__"`
    Value string       `datastore:"value"`
}

func PutValue(nameKey, value string, options ...option.ClientOption) error {
    ctx := context.Background()

    // 1. Create client.
    client, err := datastore.NewClient(ctx, os.Getenv("DATASTORE_PROJECT_ID"), options...)
    if err != nil {
        return err
    }
    defer client.Close()

    // 2. Put item.
    _, err = client.Put(
        ctx,
        datastore.NameKey("Entity", nameKey, nil),
        &Item{
            Value: value,
        },
    )
    if err != nil {
        return err
    }

    return nil
}
    main_test.go
package main

import (
    "net/http"
    "testing"

    // github.com
    "github.com/jarcoal/httpmock"
    "github.com/stretchr/testify/assert"

    // google.golang.org
    "google.golang.org/api/option"
)

func TestDatastore(t *testing.T) {
    credOption := option.WithCredentialsJSON([]byte("hoge"))

    t.Run("PutValue", func(t *testing.T) {
        mockHTTPClient := &http.Client{}
        httpmock.ActivateNonDefault(mockHTTPClient)
        defer httpmock.Reset()

        err := PutValue(
            "xxxxxxxxxxx",
            "example",
            credOption,
            option.WithHTTPClient(mockHTTPClient),
        )
        assert.Nil(t, err)
    })
}
    test
$ go test -v ./...
--- FAIL: TestDatastore (0.00s)
    --- FAIL: TestDatastore/PutValue (0.00s)
        main_test.go:29: 
                Error Trace:    main_test.go:29
                Error:          Expected nil, but got: &errors.errorString{s:"dialing: WithHTTPClient is incompatible with gRPC dial options"}
                Test:           TestDatastore/PutValue
FAIL
FAIL    command-line-arguments  0.036s

看了一下来源

    • ざっくり言うと http.Client で通信するわけじゃないと言うこと

https://github.com/googleapis/google-cloud-go/blob/master/datastore/datastore.go#L114
https://github.com/googleapis/google-api-go-client/blob/master/internal/settings.go#L86
https://github.com/googleapis/google-api-go-client/blob/master/transport/grpc/dial.go#L68

要测试Cloud Datastore

Cloud Datastore Emulator を使う

只需要一种中文选项,请改写以下内容:

1. 不再使用 httpmock 在 main_test.go 中。

package main

import (
    "testing"

    // github.com
    "github.com/stretchr/testify/assert"

    // google.golang.org
    "google.golang.org/api/option"
)

func TestDatastore(t *testing.T) {
    credOption := option.WithCredentialsJSON([]byte("hoge"))

    t.Run("PutValue", func(t *testing.T) {
        err := PutValue(
            "xxxxxxxxxxx",
            "example",
            credOption,
        )
        assert.Nil(t, err)
    })
}

2. 启动模拟器

$ docker run -d -it --rm --name cloud-datastore-emulator --entrypoint gcloud google/cloud-sdk:latest beta emulators datastore start --project=eg-example-01 --host-port=0.0.0.0:8081

3. 去测试

$ export DATASTORE_PROJECT_ID=eg-example-01
$ export DATASTORE_EMULATOR_HOST=datastore:8081

$ go test -v ./...
=== RUN   TestDatastore
=== RUN   TestDatastore/PutValue
--- PASS: TestDatastore (0.08s)
    --- PASS: TestDatastore/PutValue (0.08s)
PASS
ok      command-line-arguments  0.158s

将GitLab CI转化为CI/CD平台。

    .gitlab-ci.yml
image: docker:stable

stages:
  - testing

variables:
  GIT_SUBMODULE_STRATEGY: recursive
  GO_VERSION: "1.12"

Test golang:
  stage: testing

  image: golang:${GO_VERSION}

  variables:
    DOCKERIZE_VERSION: v0.6.1
    DATASTORE_EMULATOR_HOST: datastore:8081
    DATASTORE_PROJECT_ID: eg-example-01

  services:
    - name: google/cloud-sdk:latest
      alias: datastore
      entrypoint: ["gcloud", "beta", "emulators", "datastore"]
      command: ["start", "--project", "eg-example-01", "--host-port", "0.0.0.0:8081"]

  before_script:
    - curl -sfL https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz | tar -C /usr/local/bin -xzv
    - dockerize -wait tcp://${DATASTORE_EMULATOR_HOST} -timeout 1m

  script:
    - go test -v ./...
    1. 在测试时,将Cloud Datastore Emulator的容器指定为services。

当在本地进行测试时,可以指定 –host-port=localhost:8081,但是这样无法从容器外部进行通信,所以改为 –host-port=0.0.0.0:8081。

将DATASTORE_PROJECT_ID和DATASTORE_PROJECT_ID添加到环境变量中。

DATASTORE_PROJECT_ID应该设置为您在services中指定的别名作为主机名。

为了确保模拟器正常启动后再进行测试,使用dockerize进行等待。

参考了https://github.com/gcpug/nouhau/issues/8。

之后只需要进行测试。

广告
将在 10 秒后关闭
bannerAds