invalid memory address or nil pointer dereference go unit tests panic
Есть апишка (описана ниже). Она принимает некоторый title и вызывает другую, куда передает полученное значение.
func (h *Handler) postEmployeeCourse(c *gin.Context) {
token := strings.Split(c.Request.Header["Authorization"][0], " ")[1]
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
fmt.Printf("Error reading the body: %v\n", err)
return
}
req, err := http.NewRequest("PUT", viper.GetString("routs.LMS_s_endp"), bytes.NewBuffer(body))
if err != nil {
newErrorResponse(c, http.StatusUnauthorized, err.Error())
}
req.Header = map[string][]string{
"Authorization": {fmt.Sprintf("Bearer %s", token)},
}
client := http.Client{}
resp, _ := client.Do(req)
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading the body: %v\n", err)
return
}
c.JSON(http.StatusOK, statusResponse{"ok"})
}
В строке req, err := http.NewRequest("PUT", viper.GetString("routs.LMS_s_endp"), bytes.NewBuffer(body)) я обращаюсь к интерфейсу сторонней системы, который описан ниже. Т.е. реализуется некая вложенность.
func (h *Handler) postEmployeeCourse(c *gin.Context) {
userId, err := getUserId(c)
if err != nil {
newErrorResponse(c, http.StatusInternalServerError, "user is not found")
return
}
var input lms.Courses
if err := c.BindJSON(&input); err != nil {
newErrorResponse(c, http.StatusBadRequest, err.Error())
return
}
if err := h.services.LMSCourses.PostEmployeeCourse(userId, input); err != nil {
newErrorResponse(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, statusResponse{"ok"})
}
Используя Postman, Swagger их взаимодействие работает нормально.
Для интерфейса postEmployeeCourse (описан первым) был написан тест.
func TestHandler_postEmployeeCourse(t *testing.T) {
if err := godotenv.Load(); err != nil {
logrus.Fatalf("error loading env variables: %s", err.Error())
}
testTable := []struct {
name string
inputBody string
headerValue string
headerName string
token string
expectedStatusCode int
expectedRequestBody string
}{
{
name: "OK",
inputBody: `{"title": "C#"}`,
headerName: "Authorization",
headerValue: "Bearer",
token: os.Getenv("token"),
expectedStatusCode: 200,
expectedRequestBody: `{"status": "ok"}`,
},
}
for _, testCase := range testTable {
t.Run(testCase.name, func(t *testing.T) {
handler := NewHandler()
r := gin.New()
r.POST("/employee/course", handler.postEmployeeCourse)
w := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/employee/course", bytes.NewBufferString(testCase.inputBody))
req.Header.Set(testCase.headerName, fmt.Sprintf("%s %s", testCase.headerValue, testCase.token))
r.ServeHTTP(w, req)
assert.Equal(t, testCase.expectedStatusCode, w.Code)
assert.Equal(t, testCase.expectedRequestBody, w.Body.String())
})
}
}
При запуске теста вылетает ошибка invalid memory address or nil pointer dereference на строке _, err = ioutil.ReadAll(resp.Body) первого описанного интерфейса.
Если я правильно понимаю, то обращение к интерфейсу, внутри того, для которого написан тест, не происходит. Но в Golang я совсем новичок и не все могу осознать причину того, что при обычной работе проблем нет, а в тесте тут вылетела ошибка.
Ответы (1 шт):
client.Do возвращает 2 аргумента: указатель на Response и error. вы игнорируете error, но может быть в именно в нем написано почему указатель == nil. Да, указатели могут быть nil и их надо проверять на это.
нужно проверять на ошибку обязательно и желательно на nil
client := http.Client{}
resp, err := client.Do(req)
if err != nil {
// do something
}
if resp == nil {
// do another something
}
p.s. скорее всего проверка на ошибку в строке err != nil будет достаточной. если проверку прошли, то resp не будет nil-ом