30.2. 프로그램언어 고(Go)의 Unit testing 방법

프로그램언어 고(Go)에서의 테스트케이스 작성방법

Go언어에서 테스트 케이스를 작성하는 방법은 다음과 같습니다.


package calculator

import "testing"

func TestAdd(t *testing.T) {

  // 입력값과 예상 출력값 정의
  x := 1
  y := 2 
  expected := 3

  // 실제 계산하고 출력값 가져오기
  result := Add(x, y)

  // 예상 출력값과 실제 출력값 비교
  if result != expected {
    t.Errorf("Add(%d, %d) = %d; expected %d", x, y, result, expected)
  }
}

위 예제코드에서 보면, 테스트 함수 이름은 반드시 Test로 시작해야 하며, *testing.T 타입의 파라미터 t를 받아야 합니다.

테스트 코드 내에서는 다음과 같은 절차로 테스트를 진행합니다.

1. 입력 값과 예상 출력 값을 정의합니다.
2. 실제 함수를 호출하여 출력 값을 얻습니다.
3. 예상 출력값과 실제 출력값을 비교합니다. 값이 일치하지 않으면 테스트에 실패한 것으로 간주합니다.

테스트에 실패하면 t.Errorf 함수를 사용하여 어떤 값이 일치하지 않았는지 오류 메시지를 남길 수 있습니다.

이러한 방식으로 Go언어 테스트 코드를 작성하면 프로그램 기능별로 테스트할 수 있고, 코드 수정 시 정확하게 동작하는지 확인할 수 있습니다.

위 설명이 Go언어의 테스트 케이스 작성 방법을 이해하는데 도움이 되셨기를 바랍니다.

프로그램언어 고(Go)의 테스트 실행 방법

고(Go)언어의 테스트 실행 방법을 소개드리겠습니다.


package main

import "testing"

func TestSplit(t *testing.T) {
    got := Split("a:b:c", ":")
    want := []string{"a", "b", "c"}

    if !reflect.DeepEqual(want, got) {
        t.Errorf("Expected %v, got %v", want, got)
    }
}

고(Go)언어에서는 위 예제처럼 테스트 코드를 작성할 수 있습니다.

– testing 패키지를 가져오고 Test로 시작하는 함수를 만듭니다. 이 함수가 테스트용 함수입니다.

– 테스트용 함수 안에서 검증할 로직을 구현합니다. 위 예제에서는 Split 함수를 테스트하기 위해 실행결과와 기뛰결과를 비교합니다.

– t.Errorf 함수를 사용하여 실패 경우 에러를 보고합니다.

그리고 터미널에서 go test 명령을 사용하면 테스트 코드를 실행할 수 있습니다.


$ go test
PASS
ok      example.com/hi/... 0.165s

위 결과에서 볼 수 있듯이 테스트가 성공하면 PASS가 출력되고 실패하면 FAIL이 출력됩니다.

이처럼 고(Go)언어도 다른 언어와 마찬가지로 테스트 코드를 작성하고 실행할 수 있는 기능을 제공합니다.

자세한 사용법은 공식 문서를 참고하시기 바랍니다.

프로그램언어 고(Go)에서의 테스트 결과 보고 방법

Go언어에서 테스트 결과를 보고하는 방법은 주로 테스트 함수 내에서 log.Println()과 같은 로깅 함수를 사용하거나 테스트 프레임워크에서 제공하는 assert 함수를 사용합니다.

예를 들어, 다음과 같이 테스트 함수를 작성할 수 있습니다.

func TestSplit(t *testing.T) {
    got := Split("a:b:c", ":")
    want := []string{"a", "b", "c"}

    if !reflect.DeepEqual(got, want) {
        t.Errorf("Expected %v, got %v", want, got)
    }
}

위 코드에서는 Split 함수의 결과값이 예상한 값과 맞는지 확인하기 위해 reflect.DeepEqual을 사용하여 검증합니다.
맞지 않을 경우 t.Errorf를 사용하여 어떤 값을 기대했고 어떤 값을 얻었는지 알려줍니다.

또 다른 방법으로는 assert 패키지를 import하여 아래와 같이 작성할 수도 있습니다.

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

func TestSplit(t *testing.T) {
    got := Split("a:b:c", ":") 
    want := []string{"a", "b", "c"}
    
    assert.Equal(t, want, got, "Split 함수의 결과값이 일치하지 않습니다")
}

assert.Equal을 사용하면 expected, actual 값을 비교하고, 같지 않을 경우 해당 메시지를 출력합니다.
많은 경우 assert 함수를 사용하면 테스트 코드를 보다 간결하고 읽기 쉽게 작성할 수 있습니다.

이 외에도 테스트의 성공/실패 횟수를 출력하기 위해 testing 패키지의 Log 함수나, benchmark을 위한 시간 결과 출력을 위한 testing.B 타입 등 다양한 방법이 있습니다.
테스트 결과를 최대한 읽기 쉽고 다른 사람이 이해할 수 있도록 보고하는 것이 중요합니다.

위 설명이 도움이 되었기를 바랍니다.

프로그램언어 고(Go)의 Mocking 방법

Go 언어에서 mocking은 interface를 구현하는 가짜 객체를 만들어서 테스트를 하는 것을 의미합니다. Go 언어标准 라이브러리에서는 mocking을 위한 전용 패키지나 함수를 제공하지 않지만, 몇 가지 기법을 사용하여 mocking을 할 수 있습니다.


package main

import "fmt"

type Database interface {
  Get(key string) (string, error)
}

type RedisDB struct {}

func (r RedisDB) Get(key string) (string, error) {
  return "from redis", nil
}

func main() {
  db := RedisDB{}
  value, _ := db.Get("key1")
  fmt.Println(value)
}

위 예제에서 Database interface를 정의하고 RedisDB 구조체를 만들어 이 interface를 구현하였습니다.

이 인터페이스를 mocking하려면 아래처럼 가짜 객체를 구현하면 됩니다.


type MockDB struct{}

func (m MockDB) Get(key string) (string, error) {
  return "from mock", nil  
}

func TestGet(t *testing.T) {
  mockDB := MockDB{}
  value, _ := mockDB.Get("key")
  
  if value != "from mock" {
    t.Error("Must be from mock")
  }
}

MockDB는 Database interface를 구현하는 가짜 구조체입니다. 이 구조체를 사용하여 RedisDB 대신 테스트를 할 수 있습니다.

이 방법의 장점은 Go의 interface 기반 설계에 따라 mocking 구현이 간단하다는 것입니다. 단점은 모든 로직을 각각 구현해야 한다는 것입니다.

또 다른 기법으로는 아래와 같이 delegation을 사용하는 것입니다.


type MockDB struct {
  DB Database
}

func (m MockDB) Get(key string) (string, error) {
  if // state 변경조건 
    return "mocked value", nil
  } else {
    return m.DB.Get(key) 
  }
}

이 방법은 실제 DB 객체를 필드로 갖고 있고 추가 로직에 따라 mocking된 결과를 리턴할 수 있습니다. 장점은 중복 코드를 제거할 수 있다는 것인데요. 단점은 DB 객체와의 의존성입니다.

이 외에도 전용 mocking 프레임워크를 사용할 수 있습니다. 예를 들어, GoMock, mockery, counterfeiter 등이 있습니다.

지금까지 Go 언어에서의 mocking 방법에 대해 간략하게 설명해드렸습니다. 보다 자세한 내용은 공식문서나 관련 글을 참고해주시기 바랍니다.

프로그램언어 고(Go)에서의 테스트 자동화 방법

Go언어에서 테스트 자동화를 위해 주로 사용하는 도구는 testing 패키지와 mock 패키지입니다.

testing 패키지는 Go언어 표준 라이브러리의 일부로, 테스트 코드를 작성하고 실행할 수 있는 기능을 제공합니다. 대표적인 기능으로는 테스트 함수 정의, assert 함수를 통한 검증, benchmark 테스트 등이 있습니다.

func TestSplit(t *testing.T) {
    got := Split("a:b:c", ":")
    want := []string{"a", "b", "c"}

    if !reflect.DeepEqual(want, got) {
        t.Errorf("expected:%v, got:%v", want, got)
    }
}

위 코드는 Split 함수에 대한 단위 테스트 예제입니다. t.Errorf 메소드를 사용하여 기댓값과 실제 반환값을 비교하고 테스트에 실패한 경우 에러 메시지를 출력합니다.

mock 패키지는 목(Mock) 객체를 손쉽게 만들 수 있게 도와주는 도구입니다. 외부 의존 객체를 Mock 객체로 대체해 단위 테스트 시 나타날 수 있는 부작용을 방지합니다.

type EmailSender interface {
    SendEmail(string, string) error 
}

type MockEmailSender struct {
    mocks.Mock
}

func (m *MockEmailSender) SendEmail(to, message string) error {
    args := m.Called(to, message)
    return args.Error(0)
} 

위 코드는 EmailSender 인터페이스의 Mock 객체를 만드는 예제입니다. MockEmailSender는 내부적으로 mocks 패키지를 사용하여 호출 파라미터를 추적하고 반환 값을 설정할 수 있습니다.

이외에도 하위 호환성을 확인할 수 있는 Go 1.7+]의 subtests 기능, 테이블 기반 테스트를 손쉽게 작성할 수 있게 돕는 testify 패키지 등 다양한 테스트 자동화 도구들이 있습니다.

Go언어 생태계의 큰 장점은 표준 라이브러리와 언어 자체에 테스트 기능이 강력히 탑재되어 있다는 것입니다. 체계적인 테스트 주도 개발(TDD) 방법론을 효과적으로 적용할 수 있습니다. 안정적이고 지속 가능한 애플리케이션을 구축, 유지보수하는 데 큰 도움이 될 것입니다.

Leave a Comment