Google Test에서도 MBUnit의 RowTest나 NUnit의 TestCase 같은 데이터 기반 테스트라는 개념을 지원한다. Google Test에서는 이 개념을 Value-Parameterized Tests라고 부른다. 코드는 동일한데 사용하는 데이터 값 몇 개만 바뀌는 테스트 케이스가 있을 때, 코드 중복을 없애주면서도 어떤 테스트 케이스가 실패하고 성공하는지 정확하게 알려주는 유용한 기능이다.
그런데 아무래도 C# 보다는 유연성이 떨어지는(?) C++이라 쓰기가 마냥 쉽지만은 않아서 좀 정리를 해 둘 필요가 있다. 템플릿을 써서 구현한 기능이라 컴파일 에러라도 한 번 나면 좀 골치가 아프긴 하지만, Visual C++ 2008의 템플릿 관련 에러 메시지는 엄청나게 친절하기 때문에 미리 걱정할 필요는 없을 듯.
-
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <tuple>
using testing::Return;
namespace
{
// ParamType 구성
// TestWithParam<T>가 받는 테스트 데이터는 T 타입의 값 하나이기 때문에, 데이터 여러 개를 넘기려고 tuple을 사용한다.
typedef std::tr1::tuple<bool, MsoTriState, MsoAnimateByLevel, MsoTriState, MsoTriState, MsoAnimateByLevel> ParamType;
// Value-Parameterized Tests를 위해서는 비어있는 Fixture라도 하나 만들어야한다.
// TestWithParam<T>를 상속받는 것 외에는 일반적인 Test Fixture와 동일하다.
class TestEffectDataSetEffectInformationFixture
: public testing::TestWithParam<ParamType>
{
};
// Value-Parameterized Tests를 위한 테스트 메서드
// 여기서는 TEST_P 매크로를 쓴다. 일반적인 Fixture를 쓸 때에는 TEST_F 매크로를 쓰는 것과 다르다.
TEST_P(TestEffectDataSetEffectInformationFixture, TestSetEffectInformation)
{
bool recheckRun;
MsoTriState recheckRetValue;
MsoAnimateByLevel levelEffectRetValue;
MsoTriState animBgRetValue;
MsoTriState animBgExpected;
MsoAnimateByLevel levelEffectExpected;
// GetParam() 함수를 써서 테스트 데이터를 얻는다.
// 여기서는 데이터가 tuple 이라서 tie()를 써서 값을 얻는다.
std::tr1::tie(recheckRun, recheckRetValue, levelEffectRetValue, animBgRetValue, animBgExpected, levelEffectExpected) = GetParam();
MockEffectAdaptor ea;
MockEffectInformationAdaptor eia;
if (recheckRun) {
EXPECT_CALL(ea, GetAnimateBackgroundRechecked())
.WillOnce(Return(recheckRetValue));
}
else {
EXPECT_CALL(ea, GetAnimateBackgroundRechecked())
.Times(0);
}
EXPECT_CALL(eia, GetBuildByLevelEffect())
.WillOnce(Return(levelEffectRetValue));
EXPECT_CALL(eia, GetAnimateBackground())
.WillOnce(Return(animBgRetValue));
EffectData ed;
ed.SetEffectInformation(ea, eia);
ASSERT_EQ(animBgExpected, ed.AnimateBackground);
ASSERT_EQ(levelEffectExpected, ed.BuildByLevelEffect);
}
// Value-Parameterized Tests에 테스트 데이터를 제공
INSTANTIATE_TEST_CASE_P(Prefix, TestEffectDataSetEffectInformationFixture,
testing::Values(
ParamType(true, msoTrue, msoAnimateLevelNone, msoFalse, msoTrue, msoAnimateLevelNone),
ParamType(true, msoFalse, msoAnimateLevelNone, msoFalse, msoFalse, msoAnimateLevelNone),
ParamType(false, msoFalse, msoAnimateTextByAllLevels, msoFalse, msoFalse, msoAnimateTextByAllLevels),
ParamType(false, msoFalse, msoAnimateLevelNone, msoTrue, msoTrue, msoAnimateLevelNone)
));
} // anonymous namespace
테스트 실행 결과는 이런 식으로 나온다.
3>[ OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/0
3>[ RUN ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/1
3>[ OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/1
3>[ RUN ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/2
3>[ OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/2
3>[ RUN ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/3
3>[ OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/3
이 예제 코드에서는 테스트 데이터를 만들 때 그냥 Values()만 썼는데, Combine() 같은 걸 쓰면 주어진 값의 모든 조합을 생성해서 테스트해 준다거나 하는 기능은 그냥 코드 중복을 막는다는 차원을 넘어서는 멋진 기능이라고 생각된다.
그 외에 다른 사용법은 공식 문서를 보면 되는데, 실제 사용하면서 생기는 궁금증을 해결하기 위해서는 소스 코드에 주석 형태로 들어있는 사용 예제들을 보는게 좋다.
이 글은 스프링노트에서 작성되었습니다.