Post

[GO] Golang 유닛테스트 패키지 testify

golang 에서 패키지를 이용해 유닛테스트 하는 방법을 알아보겠습니다.

유닛 테스트(Unit Test)란?

유닛 테스트(Unit Test)는 소프트웨어 테스트의 한 종류로, 프로그램의 개별 단위(unit)를 검증하는 절차를 말합니다. 여기서 단위는 일반적으로 함수, 메서드, 또는 클래스와 같은 최소 기능 단위를 의미합니다. 유닛 테스트의 주요 목적은 특정 코드가 예상대로 작동하는지 확인하는 것입니다.

주요 특징

  1. 자동화: 유닛 테스트는 자동화되어 있어 개발자가 테스트를 직접 수행할 필요가 없습니다. 이는 빠르고 일관된 테스트 수행을 가능하게 합니다.
  2. 반복 가능성: 유닛 테스트는 반복적으로 실행할 수 있으며, 이는 코드 변경 시 회귀 테스트를 통해 기존 기능이 정상 작동하는지 확인할 수 있게 합니다.
  3. 격리: 유닛 테스트는 테스트 대상 코드가 외부 의존성에 영향을 받지 않도록 격리된 환경에서 실행됩니다. 이를 위해 모의 객체(mock objects)나 스텁(stubs)을 자주 사용합니다.
1
2
3
4
5
**스텁(Stub)**은 유닛 테스트에서 사용되는 테스트 더블(Test Double)의 한 종류로, 테스트 대상 코드가 의존하는 외부 컴포넌트나 시스템의 동작을 모방하는 객체입니다.

- 외부 시스템과의 의존성 제거: 테스트 대상 코드가 데이터베이스, 네트워크, 파일 시스템 등의 외부 시스템에 의존할 때, 이러한 의존성을 제거하여 테스트를 빠르고 안정적으로 수행할 수 있도록 합니다.

- 정해진 응답 반환: 테스트 대상 코드가 특정 조건에서 동작을 확인하기 위해, 스텁은 미리 정해진 응답을 반환하도록 설정됩니다.

testify란?

  • testify는 Go 언어로 작성된 단위 테스트 프레임워크입니다. testify는 Go의 내장 testing 패키지를 확장하여 더 쉽고 효율적으로 테스트를 작성할 수 있도록 다양한 기능을 제공합니다.

구조

  1. assert : 다양한 조건을 확인하는 함수를 제공합니다.
  2. require : assert와 비슷하지만, 실패할 경우 테스트를 즉시 중단시킵니다.
  3. mock : 모의 객체를 만들어 의존성을 격리하고 테스트할 수 있습니다.
  4. suite : 테스트 스위트를 정의하고 설정 및 해제 작업을 수행할 수 있습니다. (테스트 스위트 : 여러 개의 유닛 테스트를 그룹으로 묶은 것으로, 여러 테스트를 체계적으로 관리하고, 공통된 설정 작업을 한 곳에서 처리할 수 있습니다.)

설치 방법

1
go get github.com/stretchr/testify

testify 패키지 설명

assert 패키지

assert 패키지는 테스트에서 다양한 조건을 확인하는 함수를 제공합니다. 조건이 거짓이면 테스트가 실패하며, 실패한 경우에는 유용한 메시지를 출력합니다. assert 패키지는 테스트를 중단하지 않으므로 계속해서 실행됩니다.

주요 함수

  • Equal: 두 값이 동일한지 확인합니다.
  • NotEqual: 두 값이 동일하지 않은지 확인합니다.
  • True: 값이 참인지 확인합니다.
  • False: 값이 거짓인지 확인합니다.
  • Nil: 값이 nil인지 확인합니다.
  • NotNil: 값이 nil이 아닌지 확인합니다.

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func Add(a, b int) int {
	return a + b
}

func TestAdd(t *testing.T) {
	sum := Add(2, 3)
	assert.Equal(t, 5, sum)
	assert.NotEqual(t, 4, sum)
	assert.True(t, sum > 0)
}
1
2
3
4
5
$ go test -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      gounittest/assert       0.204s

require 패키지

require 패키지는 assert와 비슷하지만, 조건이 거짓일 경우 즉시 테스트를 중단시킵니다. 중요한 조건을 검증할 때 유용합니다.

주요 함수

  • Equal: 두 값이 동일한지 확인합니다.
  • NotEqual: 두 값이 동일하지 않은지 확인합니다.
  • True: 값이 참인지 확인합니다.
  • False: 값이 거짓인지 확인합니다.
  • Nil: 값이 nil인지 확인합니다.
  • NotNil: 값이 nil이 아닌지 확인합니다.

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func Subtract(a, b int) int {
	return a - b
}

func TestSubtract(t *testing.T) {
	result := Subtract(5, 3)
	require.Equal(t, 2, result)
	require.Equal(t, 3, result)
	require.NotNil(t, result)
}
1
2
3
4
5
6
7
8
9
10
11
12
$ go test -v
=== RUN   TestSubtract
    require_test.go:16: 
                Error Trace:
                Error:          Not equal:
                                expected: 3
                                actual  : 2
                Test:           TestSubtract
--- FAIL: TestSubtract (0.00s)
FAIL
exit status 1
FAIL    gounittest/require      0.171s

mock 패키지

mock 패키지는 테스트 중 외부 의존성을 격리하기 위해 모의 객체를 생성하는 도구입니다. 모의 객체를 통해 특정 메서드 호출에 대한 기대를 설정하고, 호출이 예상대로 이루어졌는지 확인할 수 있습니다.

  • 모의 객체(Mock Object) : 실제 객체를 대신하여 테스트에서 사용되는 객체로, 특정 메서드 호출에 대한 반환 값을 설정할 수 있습니다.
  • 기대(Expectation) : 특정 메서드가 어떤 인자와 함께 호출될 것인지 기대를 설정하고, 호출되었는지 확인합니다.
    • 기대 설정: 메서드 호출에 대한 기대와 반환 값을 설정합니다.
    • 기대 검증: 설정된 기대가 충족되었는지 확인합니다.

주요 함수

  • On: 특정 메서드 호출에 대한 기대를 설정합니다.
  • Return: 메서드 호출 시 반환할 값을 설정합니다.
  • AssertExpectations: 모든 설정된 기대가 충족되었는지 확인합니다.

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// Service 인터페이스 정의
type Service interface {
	GetData(id string) (string, error)
}

// MockService 구조체 정의
type MockService struct {
	mock.Mock
}

// GetData 메서드 구현
func (m *MockService) GetData(id string) (string, error) {
	args := m.Called(id)
	return args.String(0), args.Error(1)
}

func TestGetData(t *testing.T) {
	// MockService 인스턴스 생성
	mockService := new(MockService)

	// GetData 메서드 호출에 대한 기대와 반환 값 설정
	mockService.On("GetData", "123").Return("Mocked Data", nil)

	// 테스트 대상 함수 호출
	data, err := mockService.GetData("123")

	// 결과 검증
	assert.NoError(t, err)
	assert.Equal(t, "Mocked Data", data)

	// 설정된 기대가 충족되었는지 확인
	mockService.AssertExpectations(t)
}

이 예제에서 MockServiceService 인터페이스를 구현하는 모의 객체입니다. On 메서드를 사용하여 GetData 메서드가 "123" 인자와 함께 호출될 때 "Mocked Data"를 반환하도록 설정했습니다. 이후 AssertExpectations 메서드를 사용하여 모든 설정된 기대가 충족되었는지 확인합니다.

1
2
3
4
5
$ go test -v
=== RUN   TestGetData
--- PASS: TestGetData (0.00s)
PASS
ok      gounittest/mock 0.219s

suite 패키지

suite 패키지는 여러 개의 테스트를 그룹화하고, 공통 설정 및 해제 작업을 정의할 수 있도록 도와줍니다. 이를 통해 반복되는 설정 작업을 줄이고, 테스트 코드의 유지보수를 용이하게 합니다.

  • 테스트 스위트 정의 : 여러 테스트를 그룹으로 묶어 공통된 설정과 해제 작업을 관리합니다.
  • SetupSuite, TearDownSuite : 모든 테스트 전에 한 번, 후에 한 번 실행되는 설정 및 정리 작업을 정의합니다.
  • SetupTest, TearDownTest : 각 테스트 전에, 후에 실행되는 설정 및 정리 작업을 정의합니다.

주요 함수

  • SetupSuite: 모든 테스트 전에 한 번 실행됩니다. 초기 설정 작업을 여기서 수행합니다.
  • TearDownSuite: 모든 테스트 후에 한 번 실행됩니다. 모든 테스트가 끝난 후에 정리 작업을 여기서 수행합니다.
  • SetupTest: 각 테스트 전에 실행됩니다. 각 테스트마다 필요한 설정 작업을 여기서 수행합니다.
  • TearDownTest: 각 테스트 후에 실행됩니다. 각 테스트가 끝난 후에 정리 작업을 여기서 수행합니다.

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
)

// ExampleTestSuite 구조체 정의
type ExampleTestSuite struct {
	suite.Suite
	value int
}

// SetupSuite: 모든 테스트 전에 한 번 실행됩니다.
func (suite *ExampleTestSuite) SetupSuite() {
	// 초기 설정 작업
}

// TearDownSuite: 모든 테스트 후에 한 번 실행됩니다.
func (suite *ExampleTestSuite) TearDownSuite() {
	// 정리 작업
}

// SetupTest: 각 테스트 전에 실행됩니다.
func (suite *ExampleTestSuite) SetupTest() {
	suite.value = 5
}

// TearDownTest: 각 테스트 후에 실행됩니다.
func (suite *ExampleTestSuite) TearDownTest() {
	// 각 테스트 후에 실행될 정리 작업
}

// 실제 테스트 함수
func (suite *ExampleTestSuite) TestExample() {
	assert.Equal(suite.T(), 5, suite.value)
}

// 테스트 스위트를 실행
func TestExampleTestSuite(t *testing.T) {
	suite.Run(t, new(ExampleTestSuite))
}

이 예제에서 ExampleTestSuite는 테스트 스위트를 정의하는 구조체입니다. SetupTest 메서드는 각 테스트가 실행되기 전에 suite.value를 5로 설정하고, TestExample 메서드는 이 값을 확인하는 테스트를 수행합니다. SetupSuiteTearDownSuite 메서드는 모든 테스트가 시작되기 전과 후에 각각 한 번씩 실행됩니다.

1
2
3
4
5
6
7
$ go test -v
=== RUN   TestExampleTestSuite
=== RUN   TestExampleTestSuite/TestExample
--- PASS: TestExampleTestSuite (0.00s)
    --- PASS: TestExampleTestSuite/TestExample (0.00s)
PASS
ok      gounittest/suite        0.275s

요약

  • assert 패키지: 조건을 확인하고, 거짓일 경우 테스트를 실패로 보고합니다.
  • require 패키지: 조건이 거짓일 경우 테스트를 즉시 중단시킵니다.
  • mock 패키지: 외부 의존성을 격리하기 위해 모의 객체를 생성하고, 메서드 호출에 대한 기대를 설정합니다.
  • suite 패키지: 여러 테스트를 그룹화하고, 공통된 설정 및 해제 작업을 정의합니다.

Github 바로가기

This post is licensed under CC BY 4.0 by the author.