GO Lang

GO_웹 제작(Test 환경 구축)

u0jin 2020. 6. 13. 23:49

목표 : 테스트환경 구축 & 필요한 패키지 설치 & 테스트코드 실행

 

개발을 좀더 편하게 하기 위해 테스트 환경을 구축하는것을 추천합니다.

설치 - https://github.com/smartystreets/goconvey

 

smartystreets/goconvey

Go testing in the browser. Integrates with `go test`. Write behavioral tests in Go. - smartystreets/goconvey

github.com

백 그라운드에서 돌면서 자동으로 테스트를 해주기 때문에 사용하기 편리합니다.

해당 오픈소스를 설치하고

(* cmd 에   go get github.com/smartystreets/goconvey  입력하면 설치 가능)

go 파일이 있는 디렉토리에서 goconvey 를 돌리면 됩니다.

 

사실 전 이부분에서 좀 문제가 있었습니다.

goconvey 를 설치하면, bin파일에 설치가 되는데, 저는 돌리면 바로 될줄 알았는데 안되서 

goconvey.exe 파일을 .go파일이 있는곳에 넣은후, 해당 go파일이 있는곳에서 goconvey를 돌리니까 가능해졌습니다.

윈도우에서 크롬 알림을 켜짐으로 설정해두면 코드를 저장할때마다 알람이 뜨게 됩니다.

 

결과는 loaclhost:8080 에서 확인할수 있습니다.

 

goconvey 결과 - FAIL/PASS

 

go언어는 테스트 프레임워크를 내장하고 있기때문에, test코드를 인식할수 있습니다.

(* 테스트파일이 있는 디렉토리에서 go test 명령어를 실행하면 테스트 코드들을 일괄 실행할수 있습니다.)

go test 실행 

 

_test.go 파일들을 테스트 코드로 인식합니다.

테스트파일은 testing 이라는 표준패키지를 사용합니다. -> import 시켜줘야합니다.

 

파일 : app.go   테스트 파일 : app_test.go  


사용된 코드 = app.go / app_test.go / main.go

< app.go >

package myapplication

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

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

func (f *fooHandler) 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, _ := json.Marshal(user)

	w.Header().Add("content-type","application/text") 

	w.WriteHeader(http.StatusCreated)
	fmt.Fprint(w, string(data))

}


func barHandler(w http.ResponseWriter , r *http.Request){
	name := r.URL.Query().Get("name")  
	
	if name == ""{  
		name = "World" 
	}
	fmt.Fprintf(w,"Hello %s!",name)
}

func NewHttpHandler() http.Handler {   
	mux := http.NewServeMux()  
	
	mux.HandleFunc("/", indexHandler)

	mux.HandleFunc("/bar", barHandler)
	mux.Handle("/foo", &fooHandler{})
	return mux

}

 

 

< app_test.go >

package myapplication


import (
	"net/http"
	"net/http/httptest"
	"testing"
	"io/ioutil"
	"github.com/stretchr/testify/assert"

)

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(http.StatusOK, res.Code)

	data, _ := ioutil.ReadAll(res.Body)

	
	assert.Equal("Hello Ujin", string(data))

}

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

	res := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/bar", nil)

	
	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)

	assert.Equal(http.StatusOK, res.Code)
	data, _ := ioutil.ReadAll(res.Body)
	assert.Equal("Hello World!", string(data))
}


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

	res := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/bar?name=ujin", nil)

	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)

	assert.Equal(http.StatusOK, res.Code)
	data, _ := ioutil.ReadAll(res.Body)
	assert.Equal("Hello ujin!", string(data))
}


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

	res := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/foo", nil)

	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)

	assert.Equal(http.StatusBadRequest, res.Code)

}


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

	res := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/foo",
		strings.NewReader(`{"first_name":"youjin", "last_name":"kim", "email":"youjin@studnetpartner.com"}`))

	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)

	assert.Equal(http.StatusCreated, res.Code)
 
	user := new(User)
	err := json.NewDecoder(res.Body).Decode(user)
	assert.Nil(err)
	assert.Equal("youjin", user.FirstName)
	assert.Equal("kim", user.LastName)

}

 

< main.go >

package main

import (
	"net/http"
	"yujinGo/web3/myapplication"
)

func main(){
	http.ListenAndServe(":3000",myapplication.NewHttpHandler())
}

 


이제 코드를 하나씩 살펴보겠습니다.

(전부 살펴보는것이 아닌, 설명이 필요한 부분만을 가져오겠습니다.)

 

  • main.go
package main

import (
	"net/http"
	"yujinGo/web3/myapplication"
)

func main(){
	http.ListenAndServe(":3000",myapplication.NewHttpHandler())
    //myapplication 에서 handler 를 만듬
}

 

  • func TestIndexPathHandler
func TestIndexPathHandler(t *testing.T){ 
/*
	Test**** 와 같은이름으로 메서드명을 지어야함 -> 해당메서드가 테스트메서드임을 알려주는것
    
    testing package의 T포인터를 인자로 받음 -> 양식이 정해져있음
    testing.T 를 하나의 입력으로 받고, 출력은 없다.
    
*/

	assert := assert.New(t)
/*
	assert 패키지를 가져와서 사용함 -> 임포트에 추가시켜줘야함
    assert 패키지를 쓰는 이유는 해당 패키지가 가지고있는 함수들이 테스트 하기 편함
*/
    
	res := httptest.NewRecorder()  
    req := httptest.NewRequest("GET", "/", nil)
/*
	httptest 코드가 있음 -> 실제 네트워크 사용 아님
*/
	
	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)

	assert.Equal(http.StatusOK, res.Code)
/* 
	res.Code 가 StatusOK가 아닐경우 자동으로 test fail이 된다.
*/
	data, _ := ioutil.ReadAll(res.Body)
/*
	실제 바디를 읽어와서 비교할거임
    실제값은 res.Body에 있는데, 버퍼 클래스기때문에 바로 가져올수 없음 
    io/ioutil 패키지의 ReadAll을 이용해서 버퍼값을 전부 읽어옴
    
    data는 리턴값이고, 그뒤의 공백은 에러를 뜻함
*/

	assert.Equal("Hello Ujin", string(data))
/*
	data로 리턴값을 받고, 이걸 "Hello Ujin" 와 비교할것임
    data가 바이트 배열이기때문에, string 으로 변환후, 비교 가능
*/

}

 

여기서 import한 assert 패키지 는 

https://github.com/stretchr/testify/tree/master/assert

 

stretchr/testify

A toolkit with common assertions and mocks that plays nicely with the standard library - stretchr/testify

github.com

해당 사이트에서 가져오면 됩니다.

(* cmd 에 go get github.com/stretchr/testify/assert 를 압력하면 됩니다.)

 

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

	res := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/bar", nil)

	
	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)
/*
	mux 를 사용하지않으면 handler 가 제대로 경로를 인식하지 않기때문에 mux 를 사용해야함
	mux 를 사용하지 않고 barHandler(res.req) 를 사용한다면,
    req 로 저장된 경로 를 /bar 가 아닌 다른 경로 로 바꿔도 제대로 인식못함
*/


	assert.Equal(http.StatusOK, res.Code)
	data, _ := ioutil.ReadAll(res.Body)
	assert.Equal("Hello World!", string(data))
}

 

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

	res := httptest.NewRecorder()
	req := httptest.NewRequest("POST", "/foo",
		strings.NewReader(`{"first_name":"youjin", "last_name":"kim", "email":"youjin@studnetpartner.com"}`))

	mux := NewHttpHandler()
	mux.ServeHTTP(res, req)

	assert.Equal(http.StatusCreated, res.Code) 
/*
	201 Create 요청이 들어간것
    1개이상의 새로운 자원이 작성되고, 응답코드와 비교함
    200번이 아닌 201번이 return 되어야한다.
*/

	user := new(User)
	err := json.NewDecoder(res.Body).Decode(user)
	assert.Nil(err)
	assert.Equal("youjin", user.FirstName)
	assert.Equal("kim", user.LastName)

}

 

 

201_Created 결과