发布请求的单元测试在Go中不起作用

ix0qys7i  于 2023-05-11  发布在  Go
关注(0)|答案(1)|浏览(90)

我用go写了一个简单的post请求处理程序,它工作正常,但是当我运行它的单元测试时,它抛出了错误。与db的连接:

var err error
    server.DBConn, err = sql.Open("mysql", "root:root@tcp(localhost:8889)/infomatrix_project")
    defer server.DBConn.Close()
    http.HandleFunc("/reg_startup", post.RegStartup)
    http.ListenAndServe(":9000", nil)

处理程序函数:

type Startup struct {
    Name              string `json:"name"`
    Login             string `json:"login"`
    Password          string `json:"password"`
    Email             string `json:"email"`
    Description       string `json:"description"`
    Logo              string `json:"logo"`
    LowestInvestment  int    `json:"lowestInvestment"`
    HighestInvestment int    `json:"highestInvestment"`
    Region            string `json:"region"`
    WebSite           string `json:"website"`
    Industry          string `json:"industry"`
}

func RegStartup(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    if r.Method != "POST" {
        fmt.Fprintf(w, "error: the request is not a POST type")
        return
    }
    //other.AccessSetter(w)
    var query Startup
    err := json.NewDecoder(r.Body).Decode(&query)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    stmt, err := server.DBConn.Prepare("INSERT INTO startups (name, login, password, email, " +
        "description, logo, lowest_investment, highest_investment, region, website, industry) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
    defer stmt.Close()
    if err != nil {
        log.Fatal(err)
        return
    }
    _, err = stmt.Exec(query.Name, query.Login, query.Password, query.Email, query.Description, query.Logo,
        query.LowestInvestment, query.HighestInvestment, query.Region, query.WebSite, query.Industry)
    if err != nil {
        log.Fatal(err)
        return
    }
    fmt.Fprintf(w, "data entered successfully")
}

测试功能:

func TestRegStartup(t *testing.T) {
    load := Startup{"название на кириллице", "test_startup", "test_password", "test@example.com", "This is a test startup.",
        "https://example.com/test_startup_logo.jpg", 1000, 10000, "Kazakhstan", "https://teststartup.com", "IT"}
    loadBytes, _ := json.Marshal(load)
    request, err := http.NewRequest("POST", "/reg_startup", bytes.NewBuffer(loadBytes))
    if err != nil {
        t.Fatal(err)
    }
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(postRequests.RegStartup)
    handler.ServeHTTP(rr, request)
    status := rr.Code
    if status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
        return
    }

    expected := `"data entered successfully"`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
        return
    }
}

错误:

--- FAIL: TestRegStartup (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x102534408]

goroutine 18 [running]:
testing.tRunner.func1.2({0x1025bfa40, 0x102723f80})
        /usr/local/go/src/testing/testing.go:1526 +0x1c8
testing.tRunner.func1()
        /usr/local/go/src/testing/testing.go:1529 +0x384
panic({0x1025bfa40, 0x102723f80})
        /usr/local/go/src/runtime/panic.go:884 +0x204
database/sql.(*DB).conn(0x1025a4c9c?, {0x1025f75b8?, 0x1400009c018?}, 0xe0?)
        /usr/local/go/src/database/sql/sql.go:1282 +0x28
database/sql.(*DB).prepare(0x140000e8190?, {0x1025f75b8, 0x1400009c018}, {0x102549c1b, 0xb1}, 0x0?)
        /usr/local/go/src/database/sql/sql.go:1587 +0x34
database/sql.(*DB).PrepareContext.func1(0xa0?)
        /usr/local/go/src/database/sql/sql.go:1561 +0x48
database/sql.(*DB).retry(0x140000d5c28?, 0x140000d5be8)
        /usr/local/go/src/database/sql/sql.go:1538 +0x4c
database/sql.(*DB).PrepareContext(0x140000e8168?, {0x1025f75b8?, 0x1400009c018?}, {0x102549c1b?, 0x140000d5c01?})
        /usr/local/go/src/database/sql/sql.go:1560 +0x74
database/sql.(*DB).Prepare(0x140000e8140?, {0x102549c1b?, 0x140000baaa0?})
        /usr/local/go/src/database/sql/sql.go:1577 +0x40
github.com/SayatAbdikul/rest_api_for_startup/postRequests.RegStartup({0x1025f73a0, 0x140000a8ac0}, 0x14000104000)
        /Users/sayat/Documents/GitHub/rest_api_for_startup/postRequests/regStartup.go:38 +0x1d4
net/http.HandlerFunc.ServeHTTP(...)
        /usr/local/go/src/net/http/server.go:2122
github.com/SayatAbdikul/rest_api_for_startup/package_test_test.TestRegStartup(0x14000082d00)
        /Users/sayat/Documents/GitHub/rest_api_for_startup/package_test/regStartup_test.go:37 +0x1c0
testing.tRunner(0x14000082d00, 0x1025f4db8)
        /usr/local/go/src/testing/testing.go:1576 +0x10c
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:1629 +0x368
FAIL    github.com/SayatAbdikul/rest_api_for_startup/package_test       0.515s
--- FAIL: TestRegStartup (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x102371168]

goroutine 18 [running]:
testing.tRunner.func1.2({0x102467a40, 0x1025cbfa0})
        /usr/local/go/src/testing/testing.go:1526 +0x1c8
testing.tRunner.func1()
        /usr/local/go/src/testing/testing.go:1529 +0x384
panic({0x102467a40, 0x1025cbfa0})
        /usr/local/go/src/runtime/panic.go:884 +0x204
database/sql.(*DB).conn(0x10244cc9c?, {0x10249f5b8?, 0x14000112018?}, 0x60?)
        /usr/local/go/src/database/sql/sql.go:1282 +0x28
database/sql.(*DB).prepare(0x14000160190?, {0x10249f5b8, 0x14000112018}, {0x1023f1c8b, 0xb1}, 0x0?)
        /usr/local/go/src/database/sql/sql.go:1587 +0x34
database/sql.(*DB).PrepareContext.func1(0x40?)
        /usr/local/go/src/database/sql/sql.go:1561 +0x48
database/sql.(*DB).retry(0x1400014bc18?, 0x1400014bbd8)
        /usr/local/go/src/database/sql/sql.go:1538 +0x4c
database/sql.(*DB).PrepareContext(0x14000160168?, {0x10249f5b8?, 0x14000112018?}, {0x1023f1c8b?, 0x1400014bc01?})
        /usr/local/go/src/database/sql/sql.go:1560 +0x74
database/sql.(*DB).Prepare(0x14000160140?, {0x1023f1c8b?, 0x14000130b40?})
        /usr/local/go/src/database/sql/sql.go:1577 +0x40
github.com/SayatAbdikul/rest_api_for_startup/postRequests.RegStartup({0x10249f3a0, 0x14000120ac0}, 0x1400017c000)
        /Users/sayat/Documents/GitHub/rest_api_for_startup/postRequests/regStartup.go:38 +0x1d4
net/http.HandlerFunc.ServeHTTP(...)
        /usr/local/go/src/net/http/server.go:2122
github.com/SayatAbdikul/rest_api_for_startup/postRequests_test.TestRegStartup(0x1400011e9c0)
        /Users/sayat/Documents/GitHub/rest_api_for_startup/postRequests/regStartup_test.go:38 +0x1c0
testing.tRunner(0x1400011e9c0, 0x10249cdc0)
        /usr/local/go/src/testing/testing.go:1576 +0x10c
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:1629 +0x368
FAIL    github.com/SayatAbdikul/rest_api_for_startup/postRequests       0.935s
FAIL

我尝试将连接更改为db(连接是通过ip连接到服务器,我将其更改为localhost),并尝试更改test的位置。错误没有改变。

vhipe2zx

vhipe2zx1#

让我来分享一下我的解决方案,以及一些关于编写和测试Go代码的见解。通常,我遵循这种方式,但可能有更好的方法来管理它。代码包含在两个文件中。让我们从生产代码开始。

handlers/handlers.go文件

package handlers

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

type Startup struct {
    Name     string `json:"name"`
    Password string `json:"password"`
    Email    string `json:"email"`
}

func RegStartup(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    if r.Method != "POST" {
        fmt.Fprintf(w, "error: the request is not a POST type")
        return
    }
    //other.AccessSetter(w)
    var query Startup
    err := json.NewDecoder(r.Body).Decode(&query)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    dbConn := r.Context().Value("Db").(*sql.DB)
    if dbConn == nil {
        log.Fatal("database connection must be provided!")
        return
    }
    stmt, err := dbConn.Prepare(`INSERT INTO startups (name, password, email) VALUES (?, ?, ?)`)
    defer stmt.Close()
    if err != nil {
        log.Fatal(err)
        return
    }
    _, err = stmt.Exec(query.Name, query.Password, query.Email)
    if err != nil {
        log.Fatal(err)
        return
    }
    fmt.Fprintf(w, "data entered successfully")
}

这里的代码与您的代码非常相似。我简化了Startup结构以处理更少的字段。最重要的是将*sql.DB传递给处理函数。这是通过上下文来完成的,这是实现它的最佳方法之一。在main.go文件中,您可以自由设置超时时间,以便在超时时取消每个正在进行的呼叫。
请记住,当您从上下文中获取时,始终检查nil值(以及值的类型!).

handlers/handlers_test.go文件

package handlers

import (
    "context"
    "io"
    "net/http"
    "net/http/httptest"
    "strings"
    "testing"

    "github.com/DATA-DOG/go-sqlmock"
    "github.com/stretchr/testify/assert"
)

func TestRegStartup(t *testing.T) {
    db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
    defer db.Close()
    if err != nil {
        t.Errorf("err not expected while opening mock connection %v", err)
    }

    mock.ExpectPrepare(`INSERT INTO startups (name, password, email) VALUES (?, ?, ?)`)
    mock.ExpectExec(`INSERT INTO startups (name, password, email) VALUES (?, ?, ?)`).WithArgs("john doe", "123456789", "john.doe@example.com").WillReturnResult(sqlmock.NewResult(1, 0))

    w := httptest.NewRecorder()
    r := &http.Request{
        Header: make(http.Header),
    }
    r.Header.Set("Content-Type", "application/json")
    r.Method = http.MethodPost
    r.Body = io.NopCloser(strings.NewReader(`{"name": "john doe", "password": "123456789", "email": "john.doe@example.com"}`))
    r = r.WithContext(context.WithValue(r.Context(), "Db", db))

    RegStartup(w, r)

    assert.Equal(t, "data entered successfully", w.Body.String())
    if err := mock.ExpectationsWereMet(); err != nil {
        t.Errorf("not all expectations were met %v", err)
    }
}

测试代码需要更多的解释:
1.您应该依靠sqlmock包来处理数据库使用情况。您可以指定如何匹配查询/命令以及它们应该返回哪些结果。
1.您必须创建http.ResponseWriter*http.Request来调用RegStartup函数。您可以轻松使用httptest包。
1.确保按预期设置HTTP请求。
1.将*sql.DB包添加到HTTP请求的上下文。
如果使用go test -v -cover命令运行测试,一切都应该按预期工作。
当然,还有很多东西需要改进,但这一点可能是一个很好的起点,以更好的方式实现HTTP处理程序和测试。如果您需要更多细节或澄清,请随时询问,谢谢!

相关问题