기존에 있는 코드에 이어서 테스트 코드 부터 만들어 줍니다.
myapp/app_test.go
package myapp
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIndex(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Get(ts.URL)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Equal("Hello World", string(data))
}
func TestUsers(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Get(ts.URL + "/users")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "Get UserInfo")
}
...
func TestDeleteUsers(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
req, _ := http.NewRequest("DELETE", ts.URL + "/users/1", nil) // 1
resp, err := http.DefaultClient.Do(req) // 1
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
}
1 : http 자체에서 GET, POST는 지원하지만 DELETE는 지원하지 않아서 저렇게 사용해야 합니다.
이 상태에서 테스트를 해보도록 합니다.
이상한 점이 있습니다. 메소드를 보냈는데 통과가 되었기 때문인데요.
바로 아래에
data, _ := ioutil.ReadAll(resp.Body)
log.Print(string(data))
를 추가한 뒤 테스트를 해보면 확인해 볼 수 있다.
No User Id:1
라는 문구가 나왔다.
이 말은 myapp/app.go
의 getUserInfoHandler의
fmt.Fprint(w, "No User Id:", id)
부분이 호출이 되었다는 것을 의미한다.
그래서 저 부분이 GET만 호출 될 수 있도록
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler)
이 부분을
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Method("GET")
으로 수정해줍니다.
이 상태에서 테스트를 해보면
이런 문구가 뜨는데요.
200번의 status를 원했는데 405번인 Handler가 없다는 에러가 떴음을 확인 할 수 있습니다.
이제 PUT관련 Handler를 만들어주면 됩니다.
myapp/app.go
로 들어와서 MewHandler() 부분을 수정해 줍니다.
func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) // 1
id, err := strconv.Atoi(vars["id"]) // 1
if err != nil { // 1
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
retrun
}
_, ok := userMap[id] // 2
if !ok { // 2
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No User ID:", id)
return
}
delete(userMap, id) // 3
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Deleted User ID:", id)
}
// NewHandler make a new myapp handler
func NewHandler() http.Handler {
userMap = make(map[int]*User)
lastID = 0
mux := mux.NewRouter()
mux.HandleFunc("/", indexHandler)
mux.HandleFunc("/users", usersHandler).Methods("GET")
mux.HandleFunc("/users", createUserHandler).Methods("POST")
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
mux.HandleFunc("/users/{id:[0-9]+}", deleteUserInfoHandler).Methods("POST")
return mux
}
1 : 삭제할 ID를 추출하기 위해 GET방식에 쓰였던 코드와 유사합니다. GET과 마찬가지로 id를 가져오고 nil여부에 따라 결과값이 나오게 되고,
2 : id가 정상적으로 들어왔지만 user map에 없는 데이터를 지우려 할 때 체크하기 위해 사용했습니다.
3 : 여기서 부터는 정상적으로 삭제가 되었을 때 실행되는 코드입니다. delete()에서 map을 넣고, map에 해당하는 키를 집어넣으면 거기에 해당하는 data를 알아서 지워줍니다.
그리고 myapp/app_test.go
로 넘어와서 Handler를 새로 만들면 기존에 등록했던 data들은 날아가게 되는데, 더 이상 지울 data가 없음을 알려주는 코드를 추가하도록 합니다.
myapp/app_test.go
...
func TestDeleteUsers(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
req, _ := http.NewRequest("DELETE", ts.URL + "/users/1", nil) // 1
resp, err := http.DefaultClient.Do(req) // 1
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "No User ID:1")
이 상태에서 테스트를 진행해하면 PASS 되었음을 알 수 있습니다.
이제 정상적으로 DELETE가 작동하는지 확인해보죠. data가 사라졌으므로 다시 등록 해봅시다.
등록 확인 후
메소드를 DELETE로 맞추고 아래와 같은 URL을 입력 합니다.
현재 id값이 1번인 data가 없으므로 정상적으로 삭제할 수 없다는 문구가 뜨는 것을 확인 할 수 있습니다.
다시 id값을 2로 했을 때 정상적으로 삭제 되는 것을 확인 할 수 있습니다.
풀 소스
myapp/app_test.go
package myapp
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIndex(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Get(ts.URL)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Equal("Hello World", string(data))
}
func TestUsers(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Get(ts.URL + "/users")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "Get UserInfo")
}
func TestGetUserInfo(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Get(ts.URL + "/users/89")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "No User Id:89")
resp, err = http.Get(ts.URL + "/users/56")
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ = ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "No User Id:56")
}
func TestCreateUser(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"changbeom", "last_name":"song", "email":"changbeom@naver.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
user := new(User)
err = json.NewDecoder(resp.Body).Decode(user)
assert.NoError(err)
assert.NotEqual(0, user.ID)
id := user.ID
resp, err = http.Get(ts.URL + "/users/" + strconv.Itoa(id))
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
user2 := new(User)
err = json.NewDecoder(resp.Body).Decode(user2)
assert.NoError(err)
assert.Equal(user.ID, user2.ID)
assert.Equal(user.FirstName, user2.FirstName)
}
func TestDeleteUser(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
req, _ := http.NewRequest("DELETE", ts.URL+"/users/1", nil)
resp, err := http.DefaultClient.Do(req)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "No User ID:1")
resp, err = http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"changbeom", "last_name":"song", "email":"changbeom@naver.com"}`))
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
user := new(User)
err = json.NewDecoder(resp.Body).Decode(user)
assert.NoError(err)
assert.NotEqual(0, user.ID)
req, _ = http.NewRequest("DELETE", ts.URL+"/users/1", nil)
resp, err = http.DefaultClient.Do(req)
assert.NoError(err)
assert.Equal(http.StatusOK, resp.StatusCode)
data, _ = ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "Deleted User ID:1")
}
myapp/appt.go
package myapp
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/gorilla/mux"
)
// User struct
type User struct {
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
var userMap map[int]*User
var lastID int
func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Get UserInfo by /users/{id}")
}
func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
user, ok := userMap[id]
if !ok {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No User Id:", id)
return
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func createUserHandler(w http.ResponseWriter, r *http.Request) {
user := new(User)
err := json.NewDecoder(r.Body).Decode(user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
// Created User
lastID++
user.ID = lastID
user.CreatedAt = time.Now()
userMap[user.ID] = user
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
_, ok := userMap[id]
if !ok {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "No User ID:", id)
return
}
delete(userMap, id)
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Deleted User ID:", id)
}
// NewHandler make a new myapp handler
func NewHandler() http.Handler {
userMap = make(map[int]*User)
lastID = 0
mux := mux.NewRouter()
mux.HandleFunc("/", indexHandler)
mux.HandleFunc("/users", usersHandler).Methods("GET")
mux.HandleFunc("/users", createUserHandler).Methods("POST")
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
mux.HandleFunc("/users/{id:[0-9]+}", deleteUserHandler).Methods("DELETE")
return mux
}
main.go
package main
import (
"net/http"
"./myapp"
)
func main() {
http.ListenAndServe(":3000", myapp.NewHandler())
}
'프로그래밍(Web) > Golang' 카테고리의 다른 글
[바미] Go - Decorator 패턴 (0) | 2020.12.17 |
---|---|
[바미] Go - RESTful API(PUT, GET list) (0) | 2020.12.17 |
[바미] Go - RESTful API(POST) (0) | 2020.12.17 |
[바미] Go - RESTful API(GET) (0) | 2020.12.16 |
[바미] Go - File Upload 만들기. (0) | 2020.12.16 |