Goomba

[GTest] 일반적인 TEST 방법

  • etc

개발을 진행하면서 테스트는 반드시 필요한 작업 중 하나라고 생각합니다. (TDD든 뭐든.)
업무 상 가장 많이 사용하고 앞으로도 사용할 것 같은 Google Test를 정리하고자 합니다.

Google Test(이하 gtest)에서는 일반적으로 세 가지 방법의 테스트 케이스 작성 방식을 제공합니다.

  • TEST
  • TEST_F
  • TEST_P

이 세 가지 방식에 대해 정리했습니다.

TEST

TEST(TestSuiteName, TestName)

가장 기본적인 테스트 방식입니다.
독립적으로 동작하는 테스트를 작성할 수 있습니다.

공통 모듈에 대한 테스트 보다는 독립적으로 하나의 함수 또는 단순 입출력과 같은 테스트에 적합합니다.

Example

TEST(MathTest, Plus) {
    EXPECT_EQ(1 + 1, 2);
}

TEST(MathTest, Equal) {
    ASSERT_TRUE(1 == 1);
}

TEST_F

TEST_F(TestFixtureName, TestName)

TEST_F는 TEST처럼 단일 테스트도 가능하지만
공통 환경(Fixture)을 사용해야 하는 테스트에 사용할 수 있습니다.

::testing::Test를 상속한 클래스를 사용해야 하며,
SetUp/TearDown과 같은 함수를 사용하여 동일한 환경 설정 및 리소스 사용이 가능합니다.

Example

class TestFixture : public ::testing::Test
{
protected:
    void SetUp() override
    {
        std::cout << "테스트 시작 전" << std::endl;
    }

    void TearDown() override
    {
        std::cout << "테스트 종료 후" << std::endl;
    }

    int sum(int a, int b)
    {
        return a + b;
    }
};

TEST_F(TestFixture, TestCaseName)
{
    int num = 0;

    num = sum(2, 3);
    EXPECT_EQ(num, 5);
}

TEST_P

TEST_P(TestFixtureName, TestName)

TEST_P도 TEST처럼 단일 테스트도 가능합니다.
하지만 같은 기능을 여러 입력 값으로 테스트 하려는 경우에 사용할 수 있습니다.

::testing::TestWithParam<T>를 상속한 클래스를 사용해야 하며, 테스트를 만들어도 INSTANTIATE_TEST_SUITE_P를 통해 파라미터를 주지 않으면 동작하지 않습니다.
또한 내부적으로 GetParam 함수를 통해 파라미터를 받아 테스트를 수행합니다.

Example

struct TestParam
{
    int num1;
    int num2;
    int result;
    std::string str;
};

class TestFixture : public ::testing::TestWithParam<TestParam>
{
protected:
    void SetUp() override
    {
        std::cout << "테스트 시작 전" << std::endl;
    }

    void TearDown() override
    {
        std::cout << "테스트 종료 후" << std::endl;
    }
};

TEST_P(TestFixture, TestCaseName)
{
    TestParam param = GetParam();

    std::cout << param.str << std::endl;

    EXPECT_EQ(param.num1 + param.num2, param.result);
}

std::vector<TestParam> TestGroup = {
    {1, 1, 2, "Test 1"},
    {1, 2, 3, "Test 2"},
    {4, 5, 9, "Test 3"},
};

INSTANTIATE_TEST_SUITE_P(TestPrefix, TestFixture, ::testing::ValuesIn(TestGroup));

INSTANTIATE_TEST_SUITE_P

INSTANTIATE_TEST_SUITE_P(InstantiationName, TestSuiteName, param_generator);
INSTANTIATE_TEST_SUITE_P(InstantiationName, TestSuiteName, param_generator, name_generator);

INSTANTIATE_TEST_SUITE_P는 이름에서 유추할 수 있듯 TEST_P로 만들어진 테스트를 인스턴스화하는 매크로입니다.

  • InstantiationName : Prefix이며 빈 문자도 가능합니다.
  • TestSuiteName : TEST_P에서 정의된 테스트 스위트(클래스)입니다.
  • param_generator : 파라미터를 생성하는 부분입니다.
  • name_generator : 옵션 값이며, 테스트의 이름을 만드는 suffix입니다.

만들어진 테스트 케이스는 "InstantiationName/TestSuiteName/TestName/Index" 형태로 만들어집니다.
여기서 Index는 name_generator를 사용할 경우 해당 이름으로 지정됩니다.

param_generator

파라미터를 생성하는 부분은 아래와 같이 나뉜다고 한다. (다 써보지는 않음)

GeneratorDescription
Range(begin, end [, step])begin, end 구간 값
Values(v1, .., vN)지정된 값들
ValuesIn(container) / ValuesIn(begin, end)컨테이너 값 또는 범위 값
Bool()false, true 값
Combine(g1, .., gN)지정된 값들의 모든 조합을 std::tuple형태로 제공
ConvertGenerator<T>(g)중간 변환 어댑터 개념
  • 여기서 ConvertGenerator는 아직 사용해보지 않았습니다.

name_generator

말 그대로 이름을 생성하는 부분입니다.
생략하게 되면 기본적으로 Index가 붙습니다. (0, 1, 2, ...)

std::string(const testing::TestParamInfo<ParamType>&)의 형태를 사용해야 합니다.
테스트 코드에서 GetParam으로 얻는 값은 info.param으로 접근이 가능합니다.

// 람다를 사용한 방식
INSTANTIATE_TEST_SUITE_P(
    MyInstantiation, MyTestSuite,
    testing::Values(...),
    [](const testing::TestParamInfo<MyTestSuite::ParamType>& info) {
      // Can use info.param here to generate the test suffix
      std::string name = ...
      return name;
    });
  • 중요한 것은 공백이나 금지 문자를 포함해서는 안된다는 점입니다.
  • std::string이나 C문자열은 사용하지 않습니다. (금지 문자 포함)

아래와 같이 사용할 수도 있습니다.

std::string PrintTestName(const testing::TestParamInfo<TestParam>& info) {
    return info.param.str + std::to_string(info.param.result);
}

| 참고 Testing Reference