[Go] 19. 테스트와 벤치마크

2024. 3. 13. 18:08Go

반응형

1. 테스트

Golang 에서는 테스트를 지원하고 있습니다.

go test 명령을 이용하여 테스트 코드를 실행할 수 있으며,
아래와 같은 표준 규약을 가지고 있습니다.

  • 파일명이 _test.go 로 끝납니다
  • 테스트 코드는 func TestXxxxx(t *testing.T) 형태의 함수로 작성합니다
    • testing 패키지 import
    • 테스트 함수의 매개변수는 t *testing.T 하나여야 합니다

1.1. 테스트 실행

1.1.1. 테스트 전체 실행

ex28.1.go 라는 이름으로 아래와 같은 간단한 예제코드를 작성하였습니다.

package main

import "fmt"

func power(num int) int {
	return num * num
}

func main() {
	n := 5
	result := power(5)
	fmt.Printf("%d의 거듭제곱은 %d\n", n, result)
}

이제 테스트 코드를 작성해봅시다.
Go 표준 규약에 맞춰 ex28_1_test.go 라는 이름으로 아래와 같은 코드를 작성하였습니다.

t.Errorf 를 이용하여, 테스트중 에러 발생시에 대한 출력문도 정의할 수 있습니다.

package main

import "testing"

func TestPower(t *testing.T) {
	result := power(5)
	if result != 25 {
		t.Errorf("power(5)의 결과는 25입니다. 하지만 결과가 %d가 나왔습니다.", result)
	}
}

go test 명령을 사용하기 위해서는, 기존 코드가 모듈로 정의되어있어야 하므로
모듈을 생성한 후,

go mod init goplayground/ex28.1

테스트 코드를 실행합니다.
테스트가 모두 통과되었기 때문에 아래와 같이 PASS 가 출력되었습니다.

go test
PASS
ok      goplayground/ex28.1     0.496s

테스트 코드 실행 중, 오류가 발생되면 아래와 같이 에러 코드와 함께 FAIL이 출력됩니다.
--- FAIL: TestPower (0.00s)
    ex28_1_test.go:8: power(5)의 결과는 25입니다. 하지만 결과가 50가 나왔습니다.
FAIL
exit status 1
FAIL    goplayground/ex28.1     0.599s

1.1.2. 특정 테스트 함수만 실행

테스트 실행 중, -run 테스트명 옵션을 이용하면 특정 테스트만 실행시킬 수 있습니다.

go test -run TestPower
PASS
ok      goplayground/ex28.1     0.463s

1.2. assert

testify 외부 패키지의 assert

먼저, go get 명령어를 이용하여 외부 패키지를 설치합니다.

go get github.com/stretchr/testify

그 후, testify 패지키 내의 assert 패키지를 import하여 assert 패키지를 활용하여 테스트 코드를 작성합니다.

기본적으로 assert 패키지 내의 함수들은 첫번째 인자로 TestT(= *testing.T) 객체를 받지만,

func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool

assert.New(t) 로 생성한 객체를 활용하면, 맨 첫번째 인자에 TestingT를 입력하는 것을 생략할 수 있습니다.

package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestPower2(t *testing.T) {
	assert := assert.New(t)
	assert.Equal(25, power(5), "power(5)의 결과는 25입니다")
}

assert 패키지 내에는 아래와 같은 함수를 제공하고 있습니다.
조건에 만족하지 않을시 테스트 FAIL과 함께 msgAndArgs를 출력합니다.

  • func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
    • 인자로 입력받는 두 값(expected, actual)을 비교하여 같을 경우 테스트 성공
  • func Greater(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
    • expected > actual 일 경우 테스트 성공
  • func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool
    • object의 개수가 length과 같을 경우 테스트 성공
  • func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
    • object가 nil이 아닐경우 테스트 성공
  • func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool
    • expected != actual 일 경우 테스트 성공

2. 벤치마크

Go에서는 코드 성능을 검사하는 벤치마크 기능을 지원합니다.

벤치마크는 아래와 같은 표준 규약을 가지고 있습니다.

  • 파일명이 _test.go 로 끝납니다
  • 테스트 코드는 func BenchmarkXxxxx(b *testing.B) 형태의 함수로 작성합니다
    • testing 패키지 import
    • 테스트 함수의 매개변수는 `b *testing.B 하나여야 합니다

먼저 벤치마크 기능을 이용하여 성능 테스트를 하기 위한 코드를 작성하였습니다.

package main

import "fmt"

func fibonacci1(n int) int {
	if n < 0 {
		return 0
	} else if n < 2 {
		return n
	}
	return fibonacci1(n-2) + fibonacci1(n-1)
}

func fibonacci2(n int) int {
	if n < 0 {
		return 0
	} else if n < 2 {
		return n
	}
	first := 0
	second := 1
	result := 0

	for i := 2; i <= n; i++ {
		result = first + second
		first = second
		second = result
	}
	return second
}

func main() {
	fmt.Println(fibonacci1(10))
	fmt.Println(fibonacci2(10))
}

벤치마크를 돌리기 전에, fibonnaci1, fibonnaci2 함수가 원하는대로 작 돌아가는지 테스트 코드로 확인합니다.

package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestFibonnacci1(t *testing.T) {
	assert := assert.New(t)
	assert.Equal(0, fibonacci1(-1), "fibonacci1(-1) 결과는 0이어야 합니다")
	assert.Equal(0, fibonacci1(0), "fibonacci1(0) 결과는 0이어야 합니다")
	assert.Equal(1, fibonacci1(1), "fibonacci1(1) 결과는 1이어야 합니다")
	assert.Equal(21, fibonacci1(8), "fibonacci1(8) 결과는 21이어야 합니다")
	assert.Equal(55, fibonacci1(10), "fibonacci1(10) 결과는 55이어야 합니다")
}

func TestFibonnacci2(t *testing.T) {
	assert := assert.New(t)
	assert.Equal(0, fibonacci2(-1), "fibonacci2(-1) 결과는 0이어야 합니다")
	assert.Equal(0, fibonacci2(0), "fibonacci2(0) 결과는 0이어야 합니다")
	assert.Equal(1, fibonacci2(1), "fibonacci2(1) 결과는 1이어야 합니다")
	assert.Equal(21, fibonacci2(8), "fibonacci2(8) 결과는 21이어야 합니다")
	assert.Equal(55, fibonacci2(10), "fibonacci2(10) 결과는 55이어야 합니다")
}
PASS
ok      goplayground/ex28.2     0.658s

각 벤치마크 함수에서 b.N 만큼 반복문을 실행합니다.
Go에서는 N값을 적절히 증가시키면서 충분히 테스트를 하여 함수 성능을 측정해줍니다.

package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func BenchmarkFibonacci1(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fibonacci1(20)
	}
}

func BenchmarkFibonacci2(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fibonacci2(20)
	}
}

-bench . 옵션을 이용하여 현재 모듈에 있는 벤치마크를 테스트를 실행합니다.

go test -bench .
goos: darwin
goarch: arm64
pkg: goplayground/ex28.2
BenchmarkFibonacci1-10             48760             24825 ns/op
BenchmarkFibonacci2-10          167900041                7.157 ns/op
PASS
ok      goplayground/ex28.2     3.546s

fibonacci1 함수는 24825 ns 가 걸렸고, fibonnaci2 함수는 7.157ns 가 걸렸습니다.
fibonacci1 함수가 재귀함수를 사용하고 있기 때문에 성능이 현저히 떨어지는 것을 확인할 수 있었습니다.

728x90
반응형

'Go' 카테고리의 다른 글

[Go] 18. 채널  (0) 2024.03.12
[Go] 17. 고루틴  (0) 2024.03.11
[Go] 16. 에러 핸들링  (0) 2024.03.10
[Go] 15. 자료구조 - list, ring, map  (0) 2024.02.05
[Go] 14. 함수 고급편  (0) 2024.02.04