728x90
반응형
728x170

먼저 테스트 코드 부터 작성하고 시작합시다!

 

myapp/app_test.go 맨 아래에 추가해줍니다.

    func TestUpdateUser(t *testing.T) {
    assert := assert.New(t)

    ts := httptest.NewServer(NewHandler())
    defer ts.Close()

    req, _ := http.NewRequest("PUT", ts.URL+"/users", 
     strings.NewReader(`{"id":1, "first_name":"changbeom", "last_name":"song", "email":"changbeom@naver.com"}`)))
    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")

  }

먼저 StatusOK가 나오는지 확인하기 위해 밑에 두 줄은 주석처리 해둡니다.

Handler를 등록하지 않았기 때문에 정상적인 오류가 떴음을 확인할 수 있습니다.

 

myapp/app.go 에서 Upate Handler를 만들어 줍니다.

...

func updateUserHandler(w http.ResponseWriter, r *http.Request) {
  w.WriteHeader(http.StatusOK)
}

  // 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", updateUserHandler).Methods("PUT")
    mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
    mux.HandleFunc("/users/{id:[0-9]+}", deleteUserHandler).Methods("DELETE")

    return mux
} 

로 수정 해주고, 아까 작성했었던 myapp/app_test.go 부분으로 돌아와 Update 부분을 수정 해줍니다.

여기서 알아야 할 것이 값을 바꿀 값이 있으면 바꾸면 되지만 바꿀 값이 없을 때 어떻게 할 것인지 크게 2가지로 나뉜다는 점입니다.

  1. Update or Create : Upate 할 값이 없으면 새로 생성한다.
  2. Update : Upate 할 값이 없으면 Error 처리.

인데 지금은 2번 방식을 사용합니다.

    func TestUpdateUser(t *testing.T) {
    assert := assert.New(t)

    ts := httptest.NewServer(NewHandler())
    defer ts.Close()

    req, _ := http.NewRequest("PUT", ts.URL+"/users", 
     strings.NewReader(`{"id":1, "first_name":"changbeom", "last_name":"song", "email":"changbeom@naver.com"}`)))
    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")

  }

아까 주석처리 했었던 코드를 주석해제 시켜 준 뒤 myapp/app.go 에서 Upate Handler를 수정해 줍니다.

...

func updateUserHandler(w http.ResponseWriter, r *http.Request) {
  user := new(User) // 1
  err := json.NewDecoder(r.Body).Decode(user) // 1
  if err != nil {
    w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, err)
        return
    }
  userMap[user.ID] // 2
  if !ok { // 3
    w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "No User ID:", id)
        return
}

  // 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", updateUserHandler).Methods("PUT")
    mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler).Methods("GET")
    mux.HandleFunc("/users/{id:[0-9]+}", deleteUserHandler).Methods("DELETE")

    return mux
} 

1 : update에 json이 와야 하기 때문에 파싱해주기 위해, user struct를 만들어주고, 파싱해 옵니다.
2 : 해당하는 맵에 user.ID가 있는지 여부 확인 해줍니다.
3 : 없을 때 처리 부분.

 

이 상태에서 테스트를 해봅시다!

data가 없는 상태에서 PASS OK가 나왔기 때문에 "No User ID:", id가 출력되었다는 것을 알 수 있습니다.

 

이제 data를 create해서 실제로 Update 되는지 확인하는 코드를 작성하려 합니다.

myapp/app_test.go로 돌아와서 코드를 수정해 줍니다.

    func TestUpdateUser(t *testing.T) {
    assert := assert.New(t)

    ts := httptest.NewServer(NewHandler())
    defer ts.Close()

    req, _ := http.NewRequest("PUT", ts.URL+"/users", 
     strings.NewReader(`{"id":1, "first_name":"changbeom", "last_name":"song", "email":"changbeom@naver.com"}`)))
    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", // 1
    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)  // 1

    updateStr := fmt.Sprintf(`{"id": %d, "first_name":"updated"}`, userID) // 2   
    req, _ := http.NewRequest("PUT", ts.URL+"/users", strings.NewReader(updateStr))
    resp, err := http.DefaultClient.Do(req)
    assert.NoError(err)
    assert.Equal(http.StatusCreated, resp.StatusCode)

    user := new(User) // 3
    err = json.NewDecoder(resp.Body).Decode(user)
    assert.NoError(err)
  }

1 : POST Handler의 코드를 복붙했습니다.
2 : id값을 동적으로 바꾸어주어야 하기 때문에 문자열을 다시 만들어 주었고, first_name만 변경되도록 하였습니다.
3 : Update된 유저의 정보를 가져옵니다.

 

여기까지 하고 테스트를 진행 해봅시다!

유저의 정보가 있는지 없는지에 대한 코드를 작성했지 Update에 대한 코드를 작성하지 않았기 때문에 난 에러 입니다.

myapp/app.go로 돌아와서 코드를 수정해줍니다.

  func updateUserHandler(w http.ResponseWriter, r *http.Request) {
    user := new(User) // 1
    err := json.NewDecoder(r.Body).Decode(user)
    if err != nil {
      w.WriteHeader(http.StatusOK)
      fmt.Fprint(w, err)
      return
    }
    userMap[user.ID]
    if !ok {
      w.WriteHeader(http.StatusOK)
      fmt.Fprint(w, "No User ID:", id)
      return
  }
  _, ok := userMap[updateUser.ID] // 1
  if !ok {
    w.Writeheader(http.StatusOK)
    fmt.Fprint(w, "No User ID:", updateUser.ID)
  }
  userMap[updateUser.ID] = updateUser
  w.Header().Add("Content-Type", "application/json")
  w.WriteHeader(http.StatusOK) // 2
  data, _: = json.Marshal(updateUser) // 3
  fmt.Fprint(w, string(data)) // 3

1 : 그 id에 해당하는 것에 클라이언트가 원하는 값으로 넣어 줍니다.
2 : 바꾸어 주었으니 정상 처리 메세지를 보내주고,
3 : 그 데이터를 Marshal()해서 나온 데이터를 써줍니다.

 

이 상태로 테스트를 해보면

PASS가 떴음을 확인 할 수 있습니다.

이제 update가 잘 되는지 확인해보아야 하는데 myapp/app_test.go로 돌아와서 코드를 수정해 줍니다.

    func TestUpdateUser(t *testing.T) {
    assert := assert.New(t)

    ts := httptest.NewServer(NewHandler())
    defer ts.Close()

    req, _ := http.NewRequest("PUT", ts.URL+"/users", 
     strings.NewReader(`{"id":1, "first_name":"changbeom", "last_name":"song", "email":"changbeom@naver.com"}`)))
    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) 

    updateStr := fmt.Sprintf(`{"id": %d, "first_name":"updated"}`, userID) 
    req, _ := http.NewRequest("PUT", ts.URL+"/users", strings.NewReader(updateStr))
    resp, err := http.DefaultClient.Do(req)
    assert.NoError(err)
    assert.Equal(http.StatusCreated, resp.StatusCode)

    user := new(User)
    err = json.NewDecoder(resp.Body).Decode(user)
    assert.NoError(err)
    // 수정된 부분
    assert.Equal(updateUser.ID, user.ID) // 1
    assert.Equal("updated", upadateUser.FirstName) // 2
    assert.Equal(user.LastName, user.LastName) // 3
    assert.Equal(user.Email, user.Email) // 4
  }

1 : ID가 일치한 지 확인하고,
2 : FirstName값이 updated로 정상적으로 바뀌었는지 확인하고,
3 : LastName은 수정하지 않았으므로 같은지 확인하고.
4 : Email도 같은지 확인합니다.

 

이 상태에서 테스트를 진행해보면 LastName, Email이 아무것도 없는 빈 값으로 변해 있는 것을 확인할 수 있는데요.
지워진 이유는 Update 패킷을 보낼 때 first_name만 셋팅해서 보냈다. 그렇게 되면 JSON파서가 파싱을 할 때
Update된 정보를 읽을 때 오지 않은 나머지 data를 default값으로 바꿔버립니다.

그렇기 때문에 String의 default값인 ""값이 나온 것입니다.

 

근데 그 상태에서 userMap[updateUser.ID] = updateUser로 덮어져서 기존의 data가 사라진 것입니다.

그래서 기존의 Update되지 않는 data를 유지한 채로 Update 되도록 수정해야 합니다.

 

myapp/app.go로 돌아와서 코드를 수정 해줍니다.

  func updateUserHandler(w http.ResponseWriter, r *http.Request) {
    user := new(User) // 1
    err := json.NewDecoder(r.Body).Decode(user)
    if err != nil {
      w.WriteHeader(http.StatusOK)
      fmt.Fprint(w, err)
      return
    }
    userMap[user.ID]
    if !ok {
      w.WriteHeader(http.StatusOK)
      fmt.Fprint(w, "No User ID:", id)
      return
  }
  user , ok := userMap[updateUser.ID]
  if !ok {
    w.Writeheader(http.StatusOK)
    fmt.Fprint(w, "No User ID:", updateUser.ID)
    return
  }
  if updateUser.FirstName != "" { // 1
    user.FirstName = updateUser.FirstName
  }
  if updateUser.LastName != "" {
    user.LastName = updateUser.LastName
  }
  if updateUser.Email != "" {
    user.Email = updateUser.Email
  }
  // userMap[updateUser.ID] = updateUser // 2
  w.Header().Add("Content-Type", "application/json")
  w.WriteHeader(http.StatusOK)
  data, _: = json.Marshal(user) // 3
  fmt.Fprint(w, string(data))

1 : 빈 문자열이 아닌경우가 사용자가 바꾸길 원하는 data를 넣었을 때 이므로 빈 문자열이 아닐 때 Update 시켜줍니다.

2 : 이렇게 바꾸어주면 user값이 포인터 타입이기 때문에 포인터 타입의 값을 바꿔주면 실제 userMap[updateUser.ID]값이 바뀌는 것이기 때문에 있을 필요가 없습니다.
물론 지우지 않아도 달라지는 값이 없기 때문에 달라질 것은 없습니다.

3 : 그래서 Marshal을 해줄 때 기존 user의 데이터를 해주면 됩니다.

이 상태에서 테스트를 진행 하면 PASS가 뜨는 것을 확인 할 수 있습니다.

 

PASS가 되었지만 여기서 또 다른 문제가 있는 게 myapp/app_test.go에서

  updateStr := fmt.Sprintf(`{"id": %d, "first_name":"updated", "last_name":""}`, userID) 
  assert.Equal("", user.LastName)

으로 해서 last_name값을 ""으로 바꾸어 주려하면 error가 나면서 바뀌지 않습니다.

그 이유는 빈 문자열과 default값을 비교 할 수 없기 때문인데요.

그래서 이런 경우에 어떻게 하냐면 update용 struct를 따로 만듭니다.

myapp/app.go

 type UpdateUser struct {
    ID        int       `json:"id"`
    UpdateFirstName bool `json:"updated_first_name"`
    FirstName string    `json:"first_name"`
    UpdateLastName bool `json:"updated_last_name"`
    LastName  string    `json:"last_name"`
    UpdateEmail bool `json:"updated_email"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"created_at"`
  }

이런식으로 따로 만든 뒤 Update의 여부의 플래그를 하나씩 만들어서 Update를 받고 싶은 것만 true로 받게 할 수 있지만 복잡하기 때문에 '이렇게 쓸 수 도 있다.' 정도로만 알아두면 됩니다.

 

이렇게 PUT부분은 끝이 났고, 마지막으로 GET으로 전체 list를 가져오는 걸 안했기 때문에 이것을 구현해봅시다!

 

usersHandler가 호출 되었을 때

  func usersHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Get UserInfo by /users/{id}")
  }

Get UserInfo by /users/{id}이 문자열만 출력되게 했는데 user의 list가 출력 되게 해야 합니다.

  func usersHandler(w http.ResponseWriter, r *http.Request) {
      if len(userMap) == 0 { // 1
          w.Writeheader(http.StatusOK)
          fmt.Fprint(w, "No Users")
          return
      }
  }

1 : user의 list가 없는 경우 처리.

 

그리고 myapp/app_test.goTestUsers()함수도 수정해줍니다.

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), "No Users")
}

이 때 테스트를 하면 PASS OK가 뜰 것이고, 이어서 맨 아랫줄에 TestUsers_WithUsersData()를 만들어 줍니다.
users를 실제로 등록을 한 다음에, 다시 users를 보냈을 때 list가 나와야 하기 때문에 만들어 준 것입니다.

func TestUsers_WithUsersData(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)

      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)

    resp, err := http.Get(ts.URL+"/users")
    assert.NoError(err)
    assert.Equal(http.StatusOK, resp.StatusCode)
    data, err := ioutil.ReadAll(resp.Body)
    assert.NoError(err)
    assert.NoZero(len(data))
}

이 때 테스트를 해보면

0가 아니여야 하는데 0라고 뜹니다. 그 이유는 데이터를 보내지 않았기 때문입니다.

 

myapp/app.go에서 아까 수정했던 부분을 보게되면

  func usersHandler(w http.ResponseWriter, r *http.Request) {
      if len(userMap) == 0 { // 1
          w.Writeheader(http.StatusOK)
          fmt.Fprint(w, "No Users")
          return
      }
  }

0이 아닌 경우에 아무것도 안하기 때문에 여기에 userlist를 알려주는 코드를 수정 해주면 됩니다.

  func usersHandler(w http.ResponseWriter, r *http.Request) {
      if len(userMap) == 0 { // 1
          w.Writeheader(http.StatusOK)
          fmt.Fprint(w, "No Users")
          return
      }
      user := []*User{} // 1
      for _, u := range userMap { // 2
          users = append(users, u)
      }
      data, _ := json.Marshal(users)
      w.Header().Add("Content-Type", "application/json")
      w.WriteHeader(http.StatusOK)
      fmt.Fprint(w, string(data))
  }

1 : user list
2 : user map을 돌면서 user list를 채워주는 for문 입니다.

이 상태에서 테스트를 하면 PASS OK가 뜨게 됩니다.

 

이제 myapp/app_test.go로 돌아와서 data를 받았을 때 잘 받아지는지 확인해야 합니다.

func TestUsers_WithUsersData(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)

      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)

    resp, err := http.Get(ts.URL+"/users")
    assert.NoError(err)
    assert.Equal(http.StatusOK, resp.StatusCode)
    data, err := ioutil.ReadAll(resp.Body)

    // 수정된 부분
    users := []*User{}
    json.newDecoder(resp.Body).Decode(&users) // 1
    assert.NoError(err)
    assert.Equal(2, len(users))
    // assert.NoError(err)
    // assert.NoZero(len(data))
}

기존 두 줄 코드는 지우고 새로운 코드를 넣었습니다.

1 : marshal할 때 포인터 타입을 넣어주어야 하는데 []*User{}가 포인터 타입이 아니여서 users앞에 &를 넣어주었습니다.

 

이제 정상적으로 List를 뽑아내는지 확인해봅시다!

 

먼저 POST메소드로 id가 1,2만 바꾸어서 등록해봅시다!

그 후 GET메소드를 호출 했을 때 전체 List가 나오는지 확인 해봅시다!

풀 소스


myapp/test_app.go

package myapp

import (
    "encoding/json"
    "fmt"
    "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.Equal(string(data), "No Users")
}

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")
}

func TestUpdateUser(t *testing.T) {
    assert := assert.New(t)

    ts := httptest.NewServer(NewHandler())
    defer ts.Close()

    req, _ := http.NewRequest("PUT", ts.URL+"/users",
        strings.NewReader(`{"id":1, "first_name":"updated", "last_name":"updated", "email":"updated@naver.com"}`))
    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)

    updateStr := fmt.Sprintf(`{"id":%d, "first_name":"jason"}`, user.ID)

    req, _ = http.NewRequest("PUT", ts.URL+"/users",
        strings.NewReader(updateStr))
    resp, err = http.DefaultClient.Do(req)
    assert.NoError(err)
    assert.Equal(http.StatusOK, resp.StatusCode)

    updateUser := new(User)
    err = json.NewDecoder(resp.Body).Decode(updateUser)
    assert.NoError(err)
    assert.Equal(updateUser.ID, user.ID)
    assert.Equal("jason", updateUser.FirstName)
    assert.Equal(user.LastName, updateUser.LastName)
    assert.Equal(user.Email, updateUser.Email)
}

func TestUsers_WithUsersData(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)

    resp, err = http.Post(ts.URL+"/users", "application/json",
        strings.NewReader(`{"first_name":"jason", "last_name":"park", "email":"jason@naver.com"}`))
    assert.NoError(err)
    assert.Equal(http.StatusCreated, resp.StatusCode)

    resp, err = http.Get(ts.URL + "/users")
    assert.NoError(err)
    assert.Equal(http.StatusOK, resp.StatusCode)

    users := []*User{}
    err = json.NewDecoder(resp.Body).Decode(&users)
    assert.NoError(err)
    assert.Equal(2, len(users))
}

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) {
    if len(userMap) == 0 {
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "No Users")
        return
    }
    users := []*User{}
    for _, u := range userMap {
        users = append(users, u)
    }
    data, _ := json.Marshal(users)
    w.Header().Add("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    fmt.Fprint(w, string(data))
}

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)
}

func updateUserHandler(w http.ResponseWriter, r *http.Request) {
    updateUser := new(User)
    err := json.NewDecoder(r.Body).Decode(updateUser)
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        fmt.Fprint(w, err)
        return
    }
    user, ok := userMap[updateUser.ID]
    if !ok {
        w.WriteHeader(http.StatusOK)
        fmt.Fprint(w, "No User ID:", updateUser.ID)
        return
    }
    if updateUser.FirstName != "" {
        user.FirstName = updateUser.FirstName
    }
    if updateUser.LastName != "" {
        user.LastName = updateUser.LastName
    }
    if updateUser.Email != "" {
        user.Email = updateUser.Email
    }
    w.Header().Add("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    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", updateUserHandler).Methods("PUT")
    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())
}

 

728x90
반응형
그리드형

'프로그래밍(Web) > Golang' 카테고리의 다른 글

[바미] Go - Render, Pat, Negroni  (0) 2020.12.17
[바미] Go - Decorator 패턴  (0) 2020.12.17
[바미] Go - RESTful API(DELETE)  (0) 2020.12.17
[바미] Go - RESTful API(POST)  (0) 2020.12.17
[바미] Go - RESTful API(GET)  (0) 2020.12.16
Bami