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
注意²,可能会发生我的代码无法完全编译的情况。我有一个依赖性问题,我现在不想修复,它阻止我看到所有错误(它在这个过程中过早停止)
- 1 回答
- 0 关注
- 86 浏览
添加回答
举报