728x90
반응형
728x170

먼저 기본틀을 만들고 시작합니다.

 

그 전에 Handler를 만들어 주어야 하는데 'myapp'이라는 폴더에 app.go라는 파일에 Handler를 만들어줍니다.

 

myapp/app.go

package myapp

import "net/http"

// NewHandler make a new myapp handler
func NewHandler() http.Handler {
  mux := http.NewServeMux()

  return mux

}

main.go

package main

import (
     "net/http"
     "./myapp"

func main() {
    http.ListenAndServe(":3000", myapp.NewHandler())
}

이 상태에서 실행시키면 아무것도 뜨지 않는 것을 확인 할 수 있습니다.

 

이 상태에서 myapp폴더 안에 테스트 코드를 만들어 봅시다!

코드를 작성하기 전에 터미널 하나를 더 추가하여 goconvey를 실행시켜 놓읍시다!

myapp/app_test.go

  package myapp

  import "testing"

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

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

    resp, err := http.Get(ts.URL) // 2
    assert.NoError(err)
    assert.Equal(http.StatusOK, resp.StatusCode) // 3
  }

1 : 저 부분을 통해 실제 웹 서버는 아니고 mock-up서버가 나오게 됩니다.
그 후 항상 닫아주어야 하기 때문에 defer로 함수가 끝나기 전에 닫아 줍니다.

2 : http.Get()안에 URL을 넣어야 하는데 test서버의 URL을 넣어주고, 이 return값은 response와 error가 나옵니다.
그래서 error가 없어야 하기 때문에 assert.NoError(err)로 작성해주고,

3 : resp.Code의 코드가 http.StatusOK 와 같아야 한다는 의미입니다.

이 상태로 저장하면 goconvey가 계속 테스트 하는 것을 볼 수 있습니다. 테스트 화면을 보면 404가 반환되는 것을 알 수 있는데

Handler를 등록하지 않아서 생긴 현상 입니다.

Handler를 등록하기 위해 myapp/app.go로 돌아옵니다.

package myapp

import "net/http"

 func indexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello World")
 }

// NewHandler make a new myapp handler
func NewHandler() http.Handler {
  mux := http.NewServeMux()

  mux.HandleFunc("/", indexHandler)
  return mux

}

이 상태에서 실행하면 빌드가 패스됐음을 알 수 있습니다.

이제 첫 번째 테스트는 끝났고, 두 번째 테스트를 해보도록 해봅시다! 결과가 Hello world가 나오는지 확인 해보도록 하려고 합니다.

다시 myapp/app_test.go로 돌아와서

  package myapp

  import "testing"

  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) // 1
    assert.Equal("Hello World", string(data)) // 2
  }

1 : ioutil을 통해 Body의 결과를 모두 읽어오고, 이 때 return 값은 data, error가 나오는데 지금은 error를 무시하기 때문에 다음과 같이 작성합니다.
2 : 읽은 data가 "Hello World"와 같아야 한다는 의미 입니다.

 

이 상태에서 저장하면 테스트가 진행 될 것이고, 아래와 같은 문구가 뜰 것입니다.

그럼 이제 TestIndex부분 테스트는 끝이 났고, 두 번째 테스트를 할 것입니다.

  package myapp

  import "testing"

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

구조는 TestIndex와 동일한데, URL만 기존 테스트 URL + /users를 추가한 상태입니다.

그 후 myapp/app.go로 돌아와 Handler를 추가 해줍니다.

package myapp

import "net/http"

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

// NewHandler make a new myapp handler
func NewHandler() http.Handler {
  mux := http.NewServeMux()

  mux.HandleFunc("/", indexHandler)
  mux.HandleFunc("/users", usersHandler)
  return mux

}

참고로 이렇게 추가해주는 이유는 mux.HandleFunc("/", indexHandler)에서 하위 핸들러가 없으면 그 최상위 핸들러가

호출 되기 때문입니다.

 

그 후 myapp/app_test.go로 돌아와서 수정해줍니다.

  package myapp

  import "testing"

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

1 : app.go에 작성 했던 문자가 포함 되어 있어야 한다는 의미입니다.

 

그 후 저장 후 테스트를 하면 위와 같은 문구가 뜰텐데

정말로 제대로 되었는지 확인 해보죠!

 

그 전에 Get UserInfo가 들어와야 하는데 Hello World가 들어온게 아닌지 테스트 실패가 나는지 확인해봅시다!


assert.Contains(string(data), "Get UserInfo") 부분을 assert.Equal("Hello World", string(data))로 바꾸어 줍니다.

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

실패가 떴습니다! gocovey 화면에서 error문구를 살펴보면

정상적인 실패 화면이 떴음을 확인 할 수 있습니다.

 

그 후 테스트를 하나 더 추가합니다.

  package myapp

  import "testing"

  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 TestUserInfo(t *testing.T) {
    assert := assert.New(t)

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

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

1 : http.Get으로 89번 유저의 정보를 가져오게 합니다.

우선 이 상태에서 PASS가 되는지 확인해보면 PASS가 뜰텐데 하위에 있는 부분이 정의가 안되어서 myapp/app.go에 있는
상위 URL인 mux.HandleFunc("/users", usersHandler) 가 호출되어 PASS가 된 것 입니다.

 

그러면 data를 검증해봅시다!

data, _ := ioutil.ReadAll(resp.Body)
assert.Contains(string(data), "User Id:89")

TestUserInfo()안에 추가해준 뒤 확인해봅시다.

Fail이 떴고,

User Id:89 값이 포함이 되지 않았다는걸 확인 할 수 있습니다.

이것을 통과 시키려면 myapp/app.go로 돌아와 Handler를 추가 해주어야 합니다.

package myapp

import (
  "fmt"
  "net/http"
)

 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 getUserInfo89Handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "User Id:89")
 }

// NewHandler make a new myapp handler
func NewHandler() http.Handler {
  mux := http.NewServeMux()

  mux.HandleFunc("/", indexHandler)
  mux.HandleFunc("/users", usersHandler)
  mux.HandleFunc("/users/89", getUserInfo89Handler)
  return mux

}

그 후 저장하면 테스트가 될 것 이고,

PASS 되었다는 것을 확인 할 수 있습니다.

문제는 Id값이 변할 수 있다는 것이다. 그렇기 때문에 usersURL뒤에 붙었을 때 파싱을 해주어야 하는데

r.URL.Path를 사용하여 추출 해낼 수 있습니다. 하지만 그 과정이 복잡하기 때문에 있는 패키지를 사용하도록 하자.

 

여기에서 자세한 내용이 나옵니다.

  go get -u github.com/gorilla/mux

이것을 카피하여 설치 해줍시다.

 

myapp/app.go의 코드를 수정합시다.

package myapp

import (
  "fmt"
  "net/http"

  "github.com/gorilla/mux"
)

 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 getUserInfo89Handler(w http.ResponseWriter, r *http.Request) {
    mux.Vars(r) // 3
    fmt.Fprint(w, "User Id:", vars["id"]) 
 }

// NewHandler make a new myapp handler
func NewHandler() http.Handler {
  mux := mux.NewRouter() // 1

  mux.HandleFunc("/", indexHandler)
  mux.HandleFunc("/users", usersHandler)
  mux.HandleFunc("/users/{id:[0-9]+}", getUserInfo89Handler) // 2
  return mux

}

1 : 이제 http.NewServeMux()가 아니라 gorillamux를 가지고 할 것 입니다.

2 : 여기에 들어가보면 해당 코드와 관련된 예제 코드가 나옵니다.

3 : mux.Vars(r)를 사용하면 알아서 파싱을 해줍니다.

 

이 상태에서 저장하면

PASS 되었다는 것을 확인 할 수 있고, users뒷부분에 id값을 적어주게 되면 거기에 해당하는 값이 파싱이 되는 것을 확인 할 수 있습니다.

풀소스


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

myapp/app.go

  package myapp

  import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
  )

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

  // NewHandler make a new myapp handler
  func NewHandler() http.Handler {
    mux := mux.NewRouter()

    mux.HandleFunc("/", indexHandler)
    mux.HandleFunc("/users", usersHandler)
    mux.HandleFunc("/users/{id:[0-9]+}", getUserInfoHandler)
    return mux
  }

main.go

  package main

  import (
    "net/http"

    "./myapp"
  )

  func main() {
    http.ListenAndServe(":3000", myapp.NewHandler())
  }
728x90
반응형
그리드형
Bami