Golang 으로 맛보는 TDD(테스트 주도 개발)
요즘 핫하다는 TDD 와 핫한 언어 Golang 으로 TDD 를 찍먹해보겠습니다.
일단 간단한 웹 어플리케이션을 만들어줄게요
개발환경은 golang version 1.16 에서 진행되었습니다.
main.go
package main
import (
"made-go/myapp"
"net/http"
)
func main() {
http.ListenAndServe(":3030", myapp.NewHttpHandler())
}
myapp/app.go
package myapp
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
type fooHandler struct{}
type barHandler struct{}
func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hello world")
}
func (f *fooHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "World"
}
fmt.Fprintf(w, "Hello %s", name)
}
func (b *barHandler) ServeHTTP(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, "Bad request: ", err)
return
}
user.CreatedAt = time.Now()
data, err := json.Marshal(user)
w.Header().Add("content-type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(data))
}
func NewHttpHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", indexHandler)
mux.Handle("/foo", &fooHandler{})
mux.Handle("/bar", &barHandler{})
return mux
}
이글은 테스트 하는 방법을 설명하는 글로써 mux 나 http 에 대해서 자세한 설명은 생략하겠습니다.
go 에서 test 를 하기위해서는 파일 이름에 test가 들어가야 합니다.
app_test.go 를 만들어 줍니다.
package myapp
import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestIndexPathHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("hello world", string(data))
}
func TestFooPathHandler_WithoutName(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/foo", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("Hello World", string(data))
}
func TestFooPathHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/foo?name=jongyun", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("Hello jongyun", string(data))
}
백그라운드에서 변경되는 코드를 감지하여 매번 테스팅을 해주는 goconvey 라는 라이브러리를 사용할것입니다.
https://github.com/smartystreets/goconvey
테스트 코드를 좀더 편하게 비교하고 테스트 할수 있게금 도와주는 testify 라는 라이브러리도 사용할게요
https://github.com/stretchr/testify
go get github.com/smartystreets/goconvey
go get github.com/stretchr/testify
간단한 테스트 코드를 하나 작성 해보겠습니다.
myapp/app_test.go
package myapp
import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestIndexPathHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("hello world", string(data))
}
코드를 작성하신뒤 터미널에서 goconvey 라고 실행하시면 http://localhost:8080 에서 테스트 코드가 돌아갈것입니다.
코드가 변경되면 알아서 감지해서 테스트를 돌려주니 개발하기 참 편할거 같습니다.
테스트 코드를 추가하고 일부러 에러도 띄워보겠습니다.
package myapp
import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestIndexPathHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("hello world", string(data))
}
func TestFooPathHandler_WithoutName(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/foo", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("Hello World", string(data))
}
func TestFooPathHandler(t *testing.T) {
assert := assert.New(t)
res := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/foo?name=jongyun", nil)
mux := NewHttpHandler()
mux.ServeHTTP(res, req)
assert.Equal(res.Code, http.StatusOK)
data, _ := ioutil.ReadAll(res.Body)
assert.Equal("Hello jongyun!!", string(data))
}
FAIL 이라는 문구와 함께 친절하게 어디서 input 과 output 을 비교해서 알려주네요
파이썬을 주 언어로 사용하다가 좀더 빠른 언어가 필요하여 golang 으로 입문하게 되었는데 아직까지 정말 만족하면서 쓰고 있습니다. 마치 c 와 python 을 합쳐놓은 언어 인듯하네요
'Go' 카테고리의 다른 글
Go - 삽입정렬 구현하기 (0) | 2021.10.03 |
---|---|
Go - 구조화된 로그 남기기 (0) | 2021.09.24 |
Go lang 에서 Mysql 연동하기 (0) | 2021.09.10 |
구조체 슬라이스 정렬 (0) | 2021.09.07 |
Go - 스택 메모리와 힙메모리 (0) | 2021.09.05 |
댓글
이 글 공유하기
다른 글
-
Go - 삽입정렬 구현하기
Go - 삽입정렬 구현하기
2021.10.03 -
Go - 구조화된 로그 남기기
Go - 구조화된 로그 남기기
2021.09.24 -
Go lang 에서 Mysql 연동하기
Go lang 에서 Mysql 연동하기
2021.09.10 -
구조체 슬라이스 정렬
구조체 슬라이스 정렬
2021.09.07