GET에 이어서 myapp/app_test.go를 수정 해줍니다.
myapp/app_test.go
package myapp
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"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), "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), "User Id:56")
}
func TestCreateUserInfo(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Post(resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"changbeom", "last_name":"song", "email":"changbeom2@naver.com"}`)) // 1
assert.NoError(err)
assert.Equal(http.StatusCreated, resp.StatusCode)
}
1. Post로 보낼 때 인자가 3개인데 URL, contentType, body이고, response는 response, error가 나옵니다.
test server의 URL과 contentType은 JSON, body는 지난번에 JSON 보낸 것 처럼 strings.NewReader로 작성해줍니다.
package myapp
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
type User struct { // 4
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
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)
fmt.Fprint(w, "User Id:", vars["id"])
}
func createUserHandler(w http.ResponseWriter, r *http.Request) { // 3
user := new(User)
json.NewDecoder(r.Body).Decode(user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
// Created User
user.ID = 2
user.CreateAt = time.Now()
w.WriteHeader(http.StatusCreated)
data, _ := json.Marshal(user)
fmt.Fprint(w, string(data))
}
// NewHandler make a new myapp handler
func NewHandler() http.Handler {
mux := mux.NewRouter()
mux.HandleFunc("/", indexHandler)
mux.HandleFunc("/users", usersHandler).Methods("GET") // 1
mux.HandleFunc("/users", createUserHandler).Methods("POST") // 2
mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler)
return mux
}
1. 그리고 같은 경로라도 어떤 메소드를 보내냐에 따라서 Handler가 달라져야 하기 때문에
myapp/app.go
의 코드를 수정 해줍니다!
myapp/app.go
1 : GET 메소드로 들어왔을 때 usersHandler가 실행 되도록 한다. <br />
2 : POST 메소드로 들어왔을 때 createUserHandler가 실행 되도록 한다. <br />
3 : createUserHandler부분이다. err가 nil이 아니면 에러처리, nil이 아니면 Create해준다. <br />
4 : 클라이언트가 JSON으로 보낼 때 JSON을 읽어드려야 하는데 그러기 위해 User struct를 만들어준다. <br />
그 후 터미널을 열고 go test로 실행하여 테스트를 진행해보자! <br />
<p align = "center"> <img src = "https://user-images.githubusercontent.com/33046341/93309345-5ff87280-f83e-11ea-8ed1-b355d4f33de8.png" width = 70%> </img></p>
PASS를 확인 할 수 있다.
이제 <code>myapp/app_test.go</code>로 넘어와서 제대로 데이터가 넘어왔는지 TestCreateUserInfo()부분을 수정해준다.
``` Go
func TestCreateUserInfo(t *testing.T) {
assert := assert.New(t)
ts := httptest.NewServer(NewHandler())
defer ts.Close()
resp, err := http.Post(resp, err := http.Post(ts.URL+"/users", "application/json",
strings.NewReader(`{"first_name":"changbeom", "last_name":"song", "email":"changbeom2@naver.com"}`)) // 1
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)) // 1
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)
}
1 : id값이 int값이기 때문에 string형으로 바꾸어주기 위해 strconv.Itoa(id)를 사용 했습니다.
여기서 user2를 넣어주는 이유는 resp.Body는 user의 info가 와야 하는데 JSON포맷으로 와야하기 때문에 넣어주었고,
user와 user2는 사실상 같은 것이기 때문에 assert.Equal(user.ID, user2.ID) , assert.Equal(user.FirstName, user2.FirstName)를 사용하여 비교해주었습니다.
이제 실행을 해봅시다!
이런.. 83번째 줄에서 에러가 났네요... err := json.NewDecoder(resp.Body).Decode(user2)
여기서 decode하는데 첫번째 글자가 'U'가 왔다는 의미 입니다. strings.NewReader(
{"first_name":"changbeom", "last_name":"song", "email":"changbeom2@naver.com"}
))
이 포맷을 보면 '{'가 먼저 와야 하는데 포맷이 안 맞아서 생긴 오류입니다.
우리가 아직 유저 정보를 주는 것을 구현을 하지 않았기 때문에 myapp/app.go
에 있는 getUserInfoHandler(w http.ResponseWriter, r *http.Request)
에서 fmt.Fprint(w, "User Id:", vars["id"])
의 'U'가 나온것입니다.
이 부분을 바꾸어 주어야 하는데 우선 테스트가 통과 되도록 수정 해봅시다.
myapp/app.go
var userMap map[int]*User
var lastID int
...
func getUserInfoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) // 1
id, err := strconv.Atoi(vars["id"]) // 2
if err != nil { // 3
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
user, ok := userMap[id] // 4
if !ok { // 5
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 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)
return mux
}
1 : 파싱한 값을 가져 옵니다.
2 : vars["id"]값이 string이기 때문에 string -> int로 바꾸어 주기 위해 사용했습니다.
3 : error가 있을 시 처리 부분입니다.
4 : id에 해당하는 값이 map에 있는지 확인해줍니다.
5 : 있으면 true 없으면 false.
기존에는 고정으로 사용했지만 user map을 하나 만들어서 create했던 user정보를 등록하고 있다가
그 유저 정보의 data가 있으면 그것을 return하고, 없으면 No User ID: id값이 나오도록 변경했습니다.
그 후myapp/app_test.go
부분에서 TestGetUserInfo(t *testing.T)
를 수정해줍니다.
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")
}
아직까지 89번의 userId를 만든적이 없기 떄문에 없는 id라고 뜨게 해야 합니다.
이제 실행을 해봅시다!
go test
명령어를 통해 테스트를 해보면 통과가 되었음을 알 수 있습니다.
이제 클라이언트 앱으로 등록을 한 뒤 확인을 해봅시다!
지금까지 한 것을 리마인드 해보면 특정 id에 해당하는 유저의 정보를 가져오는 것, 그게 없다면 No User id가 출력 되도록 하는 것,
POST로 정보를 JSON 형태로 보냈을 때 이 정보를 서버가 받아서 새로운 등록을 해서 그 유저의 정보를 return하게 했고, 해당 유저의 정보의 기록을 map에 기록을 했고, 그 뒤 유저가 등록되어 있어 GET으로 /users/에 똑같이 id를 붙여 보낼 시 해당하는 유저의 정보를 JSON으로 보내준 것이기 때문에 user2로 파싱을 해서 테스팅 할 수 있게 했습니다.
풀소스
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)
}
myapp/app.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))
}
// 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)
return mux
}
'프로그래밍(Web) > Golang' 카테고리의 다른 글
[바미] Go - RESTful API(PUT, GET list) (0) | 2020.12.17 |
---|---|
[바미] Go - RESTful API(DELETE) (0) | 2020.12.17 |
[바미] Go - RESTful API(GET) (0) | 2020.12.16 |
[바미] Go - File Upload 만들기. (0) | 2020.12.16 |
[바미] Go - test환경 만들기 (0) | 2020.12.16 |