1 回答

TA貢獻1111條經驗 獲得超0個贊
代碼非常薄,很難弄清楚如何對其進行測試。
我猜弗利姆齊在標題中理解了這一點。
由于/在標題中,存在一個根本性的誤解。How to write unit test for gcloud storage?
好吧,我們不應該。他們做到了。一個更好的標題是,我不是想在這里挑剔,而是解釋我試圖解決這個問題的理解。How to unit test code consuming gcloud storage?
所以無論如何,這整件事導致我寫更少的瘦代碼,這樣我就可以測試我寫的代碼,驅動存儲的行,是否做了我們期望它做的事情。
這整件事是如此復雜,沒有錫空氣,我認為它不會回答你的問題。
但無論如何,如果這有助于思考這個困難,那已經是一個勝利。
package main
import (
"context"
"flag"
"fmt"
"testing"
"time"
"cloud.google.com/go/storage"
"google.golang.org/api/option"
)
type client struct {
c *storage.Client
projectID string
}
func New() client {
return client{}
}
func (c *client) Initialize(ctx context.Context, projectID string) error {
credFilePath := "Storage credentials path."
x, err := NewClient(ctx, option.WithCredentialsFile(credFilePath))
if err == nil {
c.c = x
c.projectID = projectID
}
return err
}
func (c client) BucketExists(ctx context.Context, bucketName string) bool {
if c.c == nil {
return nil, fmt.Errorf("not initialized")
}
bucket := c.c.Bucket(bucketName)
err := bucket.Attrs(ctx)
return err == nil
}
func (c client) CreateBucket(ctx context.Context, bucketName string) (*storage.BucketHandle, error) {
if c.c == nil {
return nil, fmt.Errorf("not initialized")
}
bucket := c.c.Bucket(bucketName)
err := bucket.Create(ctx, c.projectID, nil)
if err != nil {
return nil, err
}
return bucket, err
}
func (c client) CreateBucketIfNone(ctx context.Context, bucketName string) (*storage.BucketHandle, error) {
if !c.BucketExists(bucketName) {
return c.CreateBucket(ctx, c.projectID, bucketName)
}
return c.c.Bucket(bucketName), nil
}
type clientStorageProvider interface { // give it a better name..
Initialize(ctx context.Context, projectID string) (err error)
CreateBucketIfNone(ctx context.Context, bucketName string) (*storage.BucketHandle, error)
CreateBucket(ctx context.Context, bucketName string) (*storage.BucketHandle, error)
BucketExists(ctx context.Context, bucketName string) bool
}
func main() {
flag.Parse()
cmd := flag.Arg(0)
projectID := flag.Arg(1)
bucketName := flag.Arg(2)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
client := New()
if cmd == "create" {
createBucket(ctx, client, projectID, bucketName)
} else {
// ... more
}
}
// this is the part we are going to test.
func createBucket(ctx context.Context, client clientStorageProvider, projectID, bucketName string) error {
err := client.Initialize(ctx, projectID)
if err != nil {
return err
}
return client.CreateBucketIfNone(bucketName)
// maybe we want to apply retry strategy here,
// and test that the retry was done;
}
type clientFaker struct {
initErr error
createErr error
createIfNoneErr error
bucketExistsErr error
}
func (c clientFaker) Initialize(ctx context.Context, projectID string) (err error) {
return c.initErr
}
func (c clientFaker) CreateBucketIfNone(ctx context.Context, bucketName string) (*storage.BucketHandle, error) {
return nil, c.createIfNoneErr
}
func (c clientFaker) CreateBucket(ctx context.Context, bucketName string) (*storage.BucketHandle, error) {
return nil, c.createErr
}
func (c clientFaker) BucketExists(ctx context.Context, bucketName string) bool {
return nil, c.bucketExistsErr
}
func TestCreateBucketWithFailedInit(t *testing.T) {
c := clientFaker{
initErr: fmt.Errorf("failed init"),
}
ctx := context.Background()
err := createBucket(ctx, c, "", "")
if err == nil {
t.Fatalf("should have failed to initialize the bucket")
}
}
// etc...
請注意,我不高興作為返回參數,太具體了,但我沒有使用它(我把它放在這里,因為它在那里,否則掛著),所以很難圍繞它設計一些東西。*storage.BucketHandle
注意2,可能會發生我的代碼無法完全編譯的情況。我有一個依賴性問題,我現在不想修復,它阻止我看到所有錯誤(它在這個過程中過早停止)
- 1 回答
- 0 關注
- 97 瀏覽
添加回答
舉報