lzh2nix / articles

用 issue 来管理个人博客
https://github.com/lzh2nix/articles
61 stars 13 forks source link

golang 强筋壮骨系列之 Testing(中级) #75

Open lzh2nix opened 4 years ago

lzh2nix commented 4 years ago

目录

Testing Web Apps in Go(2020.08.15) An Introduction to Testing in Go(2020.08.16) 5 simple tips and tricks for writing unit tests in(2020.08.17) Interfaces and Composition for Effective Unit Testing in Golang(2020.08.17) Go Testing Technique: Testing JSON HTTP Requests(2020.08.17) Testing Your (HTTP) Handlers in Go(2020.08.18) Go test your tests in Go with go test(2020.08.18) Lesser-Known Features of Go Test(2020.08.18)

lzh2nix commented 4 years ago

在golang中测试web application(2020.08.15)

原文: https://markjberger.com/testing-web-apps-in-golang/

这里作者举了一个比较简单的例子,使用了最原始的stdlib http

func homeHandler(db AppDatabase) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hi there, I love %s!", db.GetBacon())
    })
}

其中对Database的依赖放到参数中(依赖注入),然后对其进行简单的测试:

function TestHome(t *testing.T) {
    mockDb := MockDb{}
    homeHandle := homeHandler(mockDb)
    req, _ := http.NewRequest("GET", "", nil)
    w := httptest.NewRecorder()
    homeHandle.ServeHTTP(w, req)
    if w.Code != http.StatusOK {
        t.Errorf("Home page didn't return %v", http.StatusOK)
    }
}

再进一步抽象:

type HandleTester func(
    method string,
    params url.Values,
) *httptest.ResponseRecorder

// Given the current test runner and an http.Handler, generate a
// HandleTester which will test its given input against the
// handler.

func GenerateHandleTester(
    t *testing.T,
    handleFunc http.Handler,
) HandleTester {

    // Given a method type ("GET", "POST", etc) and
    // parameters, serve the response against the handler and
    // return the ResponseRecorder.

    return func(
        method string,
        params url.Values,
    ) *httptest.ResponseRecorder {

        req, err := http.NewRequest(
            method,
            "",
            strings.NewReader(params.Encode()),
        )
        if err != nil {
            t.Errorf("%v", err)
        }
        req.Header.Set(
            "Content-Type",
            "application/x-www-form-urlencoded; param=value",
        )
        w := httptest.NewRecorder()
        handleFunc.ServeHTTP(w, req)
        return w
    }
}

function TestHome(t *testing.T) {
    mockDb := MockDb{}
    homeHandle := homeHandler(mockDb)
    test := GenerateHandleTester(t, homeHandle)
    w := test("GET", url.Values{})
    if w.Code != http.StatusOK {
        t.Errorf("Home page didn't return %v", http.StatusOK)
    }
}

但是实际项目中可能没有直接使用原始的http,每个公司都有对其的一些简单封装,我们的一般做法的是每个服务都写一个client, 然后通过client去测试每个api的行为(httptest.NewServer)。这样从路由到权限一路全部能够check到。

lzh2nix commented 4 years ago

Go 测试简介(2020.08.16)

原文: https://tutorialedge.net/golang/intro-testing-in-go/

简单介绍了一下golang原生的test, 不过go的原生test已经很强大了。

go test // 跑测试
go test -cover // 输出覆盖率信息
go test -coverprofile=coverage.out // 导出覆盖率信息
go tool cover -html=coverage.out // 查看覆盖情况
lzh2nix commented 4 years ago

golang单元测试的5个建议(2020.08.17)

原文: https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742

lzh2nix commented 4 years ago

通过interface和composition写更高效的单元测试(2020.08.17)

原文: https://nathanleclaire.com/blog/2015/10/10/interfaces-and-composition-for-effective-unit-testing-in-golang/

基本和之前几篇文章的思路相同

lzh2nix commented 4 years ago

golang 测试http json request(2020.08.17)

原文: https://medium.com/@xoen/go-testing-technique-testing-json-http-requests-76d9ce0e11f#.95p1r8n16

http 相关的测试使用httptest.NewServer 都是常规操作。怎么去写测试这块我觉得还是直接参考比较成熟的项目就ok了,然后模仿其中的写法。

关于json我们都是marshal/unmarshal之后按照常规的对象来看下,这里作者提供了另外一种思路

reqJson, err := simplejson.NewFromReader(r.Body)

这样不用特意的去解析整个struct, 需要的字段直接通过

    lifeMeaning := reqJson.GetPath("meta", "lifeMeaning").MustInt()

这种方式提取。

快速构建json 对象:

 payload := fmt.Sprintf(`
    {
      "meta": {
        "lifeMeaning": %d
      },
      "data": {
        " “message”: "%s"
      }
    }`, LIFE_MEANING, msg)
lzh2nix commented 4 years ago

在golang中测试http handler(2020.08.18)

原文: https://blog.questionable.services/article/testing-http-handlers-go/

这里介绍了http相关的一些测试方法

测试普通http handler

func TestHealthCheckHandler(t *testing.T) {
    // Create a request to pass to our handler. We don't have any query parameters for now, so we'll
    // pass 'nil' as the third parameter.
    req, err := http.NewRequest("GET", "/health-check", nil)
    if err != nil {
        t.Fatal(err)
    }

    // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(HealthCheckHandler) // 这里是需要测试的方法

    // Our handlers satisfy http.Handler, so we can call their ServeHTTP method 
    // directly and pass in our Request and ResponseRecorder.
    handler.ServeHTTP(rr, req)

    // Check the status code is what we expect.
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // Check the response body is what we expect.
    expected := `{"alive": true}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
    }
}

测试http 中间件

func TestPopulateContext(t *testing.T) {
    req, err := http.NewRequest("GET", "/api/users", nil)
    if err != nil {
        t.Fatal(err)
    }

    testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 这个handler会在中间件之后调用,所以这里可以测试中间件的行为
        if val, ok := r.Context().Value("app.req.id").(string); !ok {
            t.Errorf("app.req.id not in request context: got %q", val)
        }
    })

    rr := httptest.NewRecorder()
    // func RequestIDMiddleware(h http.Handler) http.Handler
    // Stores an "app.req.id" in the request context.
    handler := RequestIDMiddleware(testHandler)
    handler.ServeHTTP(rr, req)
}

依赖数据库的测试

使用inerface来对数据库的抽象,而不依赖具体的数据库。

这一点确实太难了,比如你项目中依赖的是mongoDB 你怎么在测试mock mongo相关的接口就是一个很大的问题。

lzh2nix commented 4 years ago

go test 小技巧(2020.08.18)

原文: https://deadbeef.me/2018/05/go-test

主要讲一些go test中的小技巧

lzh2nix commented 4 years ago

少有人知道的go test功能(2020.08.18)

原文: https://splice.com/blog/lesser-known-features-go-test/