Programming Language/Go

[Go] 테이블 주도 테스트란? Table Driven Test

깜태 2022. 3. 23. 02:10
728x90

최근에 테스트 주도 개발(Test Driven Development, TDD)에 관해 관심이 있어서 알아보았고,

그러던 중 당근마켓에서 쓴 글을 읽다가 알게된 테이블 주도 테스트라는 것이 있어 새롭게 배운 것 같아

몇개 더 알아보다가 글을 써본다.

 

https://medium.com/daangn/how-to-write-a-testable-golang-code-4c0e67612bb8

 

유연하고 테스트 가능한 Go 코드 작성하기

Go 언어는 개발자에게 코딩의 즐거움을 일깨워주는 언어입니다. Go가 가진 명확한 한계(가비지 컬렉션으로 인한 성능 저하, 제네릭을 지원하지 않음)에도 불구하고 말이죠.

medium.com

 

먼저 TDD를 간단하게 설명을 하면 테스트를 통해 개발해보자는 의미로,

개발을 한다음에 테스트하는 구현의 중요성보다 로직이 가질 수 있는 문제점을 생각해보며 개발을 진행한다는 점에서

기존의 개발과는 접근하는 방식이 다르다.

 

TDD를 왜 쓸까에 대해서 생각해보면 그만큼의 테스트도 없으면 실전에서는 더 무너지기 때문일 거라 생각한다.

실제로 나도 테스트 코드없이 디버깅을 하면서 개발을 진행했다가 다른 사람들에게 나의 로직을 증명할 일이 생기고 보니,

나의 로직을 증명하는 테스트 코드가 필요하다는 걸 절실히 깨달았다.

(이 마저도 없으면 나는 아무 근거도 없이 나의 코드가 된다고 하는 놈이 될 수도 있다.)

 

Go에서도 TDD가 가능하지만 모처럼 특별한 개념인 테이블 주도 테스트(Table Driven Test)라는 개념이 등장했으니

무엇이 다른지 알아보았다.

 

테이블 주도 테스트 - Table Driven Test(TDT)

테이블 주도 테스트는 조금 바뀐만큼 사실은 크게 별거는 없다.

TDD(Test Driven Development) 와는 다르게 Table 이라는 개념이 추가됐는데,

여기서의 Table은 DB에서의 Table처럼 여러 개의 테스트 케이스를 한 데 묶은 경우를 테스트 기법을 테이블 주도 테스트라고 한다.

 

아래는 간단한 TDD 케이스 중 하나를 가져와봤다.
(출처 : https://velog.io/@tmdgh0221/TDD로-배우는-Golang#테스트-코드---arrays)

package sum

import "testing"

func TestSum(t *testing.T) {

	t.Run("collection of 5 numbers", func(t *testing.T) {
		numbers := []int{1, 2, 3, 4, 5}

		got := Sum(numbers)
		want := 15

		if got != want {
			t.Errorf("got %d want %d given, %v", got, want, numbers)
		}
	})

	t.Run("collection of any size", func(t *testing.T) {
		numbers := []int{1, 2, 3}

		got := Sum(numbers)
		want := 6

		if got != want {
			t.Errorf("got %d want %d given, %v", got, want, numbers)
		}
	})

}

테스트 내용을 보면 숫자들이 입력되었을 때, 그 합이 want 와 같은지 테스트하는 코드로

테스트 2번 했구나 정도로 이해하고 넘어가보자.

 

아래는 구글에서 제공하는 Table Driven Test 의 예시다.

(출처 : https://github.com/golang/go/wiki/TableDrivenTests)

var flagtests = []struct {
	in  string
	out string
}{
	{"%a", "[%a]"},
	{"%-a", "[%-a]"},
	{"%+a", "[%+a]"},
	{"%#a", "[%#a]"},
	{"% a", "[% a]"},
	{"%0a", "[%0a]"},
	{"%1.2a", "[%1.2a]"},
	{"%-1.2a", "[%-1.2a]"},
	{"%+1.2a", "[%+1.2a]"},
	{"%-+1.2a", "[%+-1.2a]"},
	{"%-+1.2abc", "[%+-1.2a]bc"},
	{"%-1.2abc", "[%-1.2a]bc"},
}
func TestFlagParser(t *testing.T) {
	var flagprinter flagPrinter
	for _, tt := range flagtests {
		t.Run(tt.in, func(t *testing.T) {
			s := Sprintf(tt.in, &flagprinter)
			if s != tt.out {
				t.Errorf("got %q, want %q", s, tt.out)
			}
		})
	}
}

테스트를 여러 개 두었는데도 코드가 깔끔해보인다.

 

TDT의 장점을 단적으로 보여주기 위해 의도적으로 코드를 가져왔다.

내 생각에는 테스트 케이스를 여러개 작성하는 것에 비해 테이블처럼 한 데 묶음으로써,

코드가 간결해지고, 가독성도 향상되는 게 장점인것 같다.

케이스를 여러 개 적을 수 있는 상황이라면 TDT가 깔끔해보인다.

 

 

참고

https://simplear.tistory.com/23

https://velog.io/@tmdgh0221/TDD로-배우는-Golang

https://github.com/golang/go/wiki/TableDrivenTests

https://medium.com/@dog156987/test-driven-development-tdd-란-무엇인가-59b7b4a65c4

https://brunch.co.kr/@freeism/19

728x90