I am trying to implement unit testing on my SignUp Handler and a call to database. However, it throws panic error on the database call in my SignUp Handler. It is a simple SignUp Handler that receives a JSON with username, password, and email. I will then use a SELECT statement to check if this username is duplicated inside the SignUp handler itself.
This all works when I am sending my post request to this handler. However, when I am actually doing unit testing, it doesn't work and threw me the 2 error messages. I feel that this is because the database wasn't initialized in the test environment but I am not sure how do do this without using third party frameworks to conduct a mock database.
error message
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
signup.go
package handler
type SignUpJson struct {
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
}
func SignUp(w http.ResponseWriter, r *http.Request) {
// Set Headers
w.Header().Set("Content-Type", "application/json")
var newUser auth_management.SignUpJson
// Reading the request body and UnMarshal the body to the LoginJson struct
bs, _ := io.ReadAll(req.Body)
if err := json.Unmarshal(bs, &newUser); err != nil {
utils.ResponseJson(w, http.StatusInternalServerError, "Internal Server Error")
log.Println("Internal Server Error in UnMarshal JSON body in SignUp route:", err)
return
}
ctx := context.Background()
ctx, cancel = context.WithTimeout(ctx, time.Minute * 2)
defer cancel()
// Check if username already exists in database (duplicates not allowed)
isExistingUsername := database.GetUsername(ctx, newUser.Username) // throws panic error here when testing
if isExistingUsername {
utils.ResponseJson(w, http.StatusBadRequest, "Username has already been taken. Please try again.")
return
}
// other code logic...
}
sqlquery.go
package database
var SQL_SELECT_FROM_USERS = "SELECT %s FROM users WHERE %s = $1;"
func GetUsername(ctx context.Context, username string) bool {
row := conn.QueryRow(ctx, fmt.Sprintf(SQL_SELECT_FROM_USERS, "username", "username"), username)
return row.Scan() != pgx.ErrNoRows
}
SignUp_test.go
package handler
func Test_SignUp(t *testing.T) {
var tests = []struct {
name string
postedData SignUpJson
expectedStatusCode int
}{
{
name: "valid login",
postedData: SignUpJson{
Username: "testusername",
Password: "testpassword",
Email: "test@email.com",
},
expectedStatusCode: 200,
},
}
for _, e := range tests {
jsonStr, err := json.Marshal(e.postedData)
if err != nil {
t.Fatal(err)
}
// Setting a request for testing
req, _ := http.NewRequest(http.MethodPost, "/signup", strings.NewReader(string(jsonStr)))
req.Header.Set("Content-Type", "application/json")
// Setting and recording the response
res := httptest.NewRecorder()
handler := http.HandlerFunc(SignUp)
handler.ServeHTTP(res, req)
if res.Code != e.expectedStatusCode {
t.Errorf("%s: returned wrong status code; expected %d but got %d", e.name, e.expectedStatusCode, res.Code)
}
}
}
setup_test.go
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
I have seen a similar question here but not sure if that is the right approach as there was no response and the answer was confusing: How to write an unit test for a handler that invokes a function that interacts with db in Golang using pgx driver?