앞서 포스팅했던 단위 테스트 관련 글들에 이어서 이번에는 C++ 에서 단위 테스트를 사용하기 위한 프레임웍으로 Google Test와 Google Mock을 간단히 소개하겠다.
Google Test : https://github.com/google/googletest
Google Mock : https://github.com/google/googletest/tree/master/googlemock
세부적인 Assertions 함수들은 아래 문서를 참조하자.
https://github.com/google/googletest/blob/master/googletest/docs/Primer.md
여기서는 Mocking 을 어떻게 구성하는지 간단한 Sample Project 를 가지고 설명한다.
https://github.com/prostars/SampleForGoogleMock
이 글을 읽는 여러분이 C++ 에 익숙하다고 믿고 큰 줄기만 정리하고 넘어가겠다.
나머지는 코드를 직접 확인하기 바라며 이 글을 읽는 동안 코드를 열어놓고 같이 보는 것을 권장한다.
Visual Studio 2013을 기준으로 Sample Project 구성하는 방법을 같이 보자.
Win32 콘솔 프로젝트를 하나 생성한다.
정적라이브러리로 설정하고 추가 옵션은 모두 해제한다.
솔루션 폴더에 3rdLib 빈 폴더를 하나 추가한다.
https://github.com/google/googletest 에서 프로젝트를 zip으로 다운로드 받는다.
받아서 압축을 풀고 위에서 생성한 3rdLib 폴더에 googletest, googlemock 두 폴더를 복사한다.
위 솔루션을 열고 프로젝트를 추가하는데 기존 프로젝트로 선택하고 googletest/msvc/gtest.vcproj 를 선택한다.
단방향 업그레이드 팝업이 열릴 텐데 진행한다. 보안 경고가 열릴 텐데 무시하고 진행한다.
빌드를 해본다. 잘 될 것이다. 에러가 발생했다면 건투를 빈다.
다시 프로젝트를 추가하는데 이번에도 기존 프로젝트를 선택하고 googlemock/msvc/2010/gmock.vcxproj 를 선택한다. 이번에도 보안 경고를 무시한다.
이번에는 버전 문제로 빌드에 바로 실패할 것이다.
방금 추가된 gmock(Visual Studio 2010) 프로젝트에서 마우스 우클릭하여 팝업 메뉴를 열고 VC++ 컴파일러 및 라이브러리 업그레이드(R) 를 선택하고 계속 진행한다.
다시 빌드를 시도하면 잘 될 것이다.
Calcurator 는 AccountStorage 에서 정보를 가지고 와서 총합과 평균을 구할 수 있는 기능을 가진다.
AccountStorage 는 월별 매출 정보가 저장되어있는 DB에 접근할 수 있는 Interface 다.
단위 테스트가 DB에 종속성을 가지는 것은 아름답지 않으므로 Mocking 해야 할 대상이다.
MockAccountStorage 에서 MOCK_METHOD(n) 매크로 함수와 같이 사용하는 ON_CALL() 매크로 함수는 MOCK_METHOD(n) 으로 설정된 메소드가 호출되었을 때에 대한 처리를 지정할 수가 있다.
여기서는 호출된 Mock 함수에 대응하는 Fake 메소드를 호출하도록 지정했다.
코드는 아래와 같다.
ON_CALL(*this, getValue(_, _)).WillByDefault(::testing::Invoke(&fake, &FakeAccountStorage::getValue));
getValue() 메소드가 어떤 파라미터 값으로 호출이 되던지 상관없이 FakeAccountStorage::getValue() 를 호출하도록 지정한 것이다.
위 매크로 함수들의 세부 기능 설명은 Google Mock 문서에서 찾을 수 있다.
class FixtureTestCalculator : public ::testing::Test {
protected:
void SetUp() override {
storage = std::make_shared<MockAccountStorage>();
storage->fake.insertValue(1, 100);
storage->fake.insertValue(2, 300);
storage->fake.insertValue(3, 200);
storage->fake.insertValue(4, 600);
storage->fake.insertValue(5, 40);
}
void TearDown() override {
storage.reset();
}
public:
std::shared_ptr<MockAccountStorage> storage;
여기서는 SetUp() 에서 MockAccountStorage 에 5개월 치의 정보를 등록하고 TearDown() 에서 MockAccountStorage 를 해제한다.
아래 코드는 위에서 이야기한 2개의 테스트 케이스에 대한 코드다.
TEST_F(FixtureTestCalculator, SuccessToSum) {
FEATURE("Calculator 는 storage 에서 월별 매출에 대한 총합을 구할 수 있다.");
SCENARIO("storage 에 저장된 월별 매출에 대한 총합을 구한다.");
GIVEN("1월 부터 5월까지 5개월에 대한 매출 정보가 있다.");
Calculator calculator(this->storage);
WHEN("총합을 구했을 때");
THEN("모든 정보를 1번만 로딩해야 하고,");
EXPECT_CALL(*(this->storage), getMonths(_)).Times(1);
int sum = 0;
calculator.sum(sum);
AND("총합은 1240 이어야 한다.");
EXPECT_EQ(sum, 1240);
}
TEST_F(FixtureTestCalculator, SuccessToAverage) {
FEATURE("Calculator 는 storage 에서 월별 매출에 대한 평균을 구할 수 있다.");
SCENARIO("storage 에 저장된 월별 매출에 대한 평균을 구한다.");
GIVEN("1월 부터 5월까지 5개월에 대한 매출 정보가 있다.");
Calculator calculator(this->storage);
WHEN("평균을 구했을 때");
THEN("모든 정보를 2번 로딩해야 하고,");
EXPECT_CALL(*(this->storage), getMonths(_)).Times(2);
double average = 0;
calculator.average(average);
AND("248 이어야 한다.");
EXPECT_EQ(average, 248);
TEST_F() 매크로 함수는 단순한 TEST() 매크로 함수와 달리 첫번째 파라미터로 Test Fixture 를 지정할 수 있는 기능을 제공한다. 두번째 파라미터는 그냥 테스트 케이스의 제목이다.
위 코드에서 보이는 GIVEN(), WHEN(), THEN() 등의 함수는 Google Test 에서 제공하는 함수가 아니고 BDD 스타일을 사용하기 위해 간단히 구현한 console print 함수들이고 TestUtil.h 에 구현되어 있다.
위에서 Google Test 와 Google Mock 에서 제공하는 기능은 EXPECT_CALL() 과 EXPECT_EQ() 다.
EXPECT_EQ() 는 지정된 파라미터 2개가 서로 같지 않으면 테스트 케이스를 실패로 처리한다.
EXPECT_CALL() 는 지정된 함수가 지정된 조건으로 호출되지 않으면 테스트 케이스를 실패로 처리한다.
여기서는 getMonths() 메소드가 총합 케이스에서는 1번, 평균 케이스에서는 2번 호출되어야 한다고 지정했다.
더 부연 설명할 만한 내용이 없는 간단한 Sample 이므로 나머지는 직접 가지고 놀아보기 바란다.
예를 들어 ON_CALL 지정을 주석 처리하고 테스트를 실행하면 테스트 케이스는 실패할 것이다.
'Dev' 카테고리의 다른 글
Setting ZeroMQ for MacOS (0) | 2018.01.12 |
---|---|
Source Tree for Mac 에서 password reset (2) | 2017.12.28 |
얼마전에 포스팅했던 'NON-BLOCKING SOCKET에 OPENSSL 적용하기'가 TOAST Meetup에 소개되었습니다. (0) | 2017.07.28 |
테스트 용이성(Testability) 향상을 위한 DI(Dependency Injection) (0) | 2017.06.19 |
BDD (Behaviour-Driven Development)에 대한 간략한 정리 (0) | 2017.04.16 |