ㄹGoogletest Primer
소개 : 왜 구글테스트인가?
구글테스트는 더 나은 C++테스트를 작성하는데 도움이 됩니다.googletest는 Google의 특정 요구 사항과 제약 사항을 염두에두고 테스팅 기술 팀에서 개발 한 테스트 프레임 워크입니다. Linux, Windows, Mac 어디서든 C++ 코드를 작성하면 googletest가 도움이 될 수 있습니다. 또한 단위 테스트뿐만 아니라 모든 종류의 테스트를 지원합니다.
명명법에 주의 하라.
참고 : Test, Test Case 및 Test Suite라는 용어를 다르게 정의하면 약간의 혼동이있을 수 있으므로이를 오해하지 마십시오.역사적으로 googletest는 관련 테스트를 그룹화하기 위해 테스트 케이스라는 용어를 사용하기 시작했지만 ISTQB (International Software Testing Qualifications Board) 자료 및 소프트웨어 품질에 대한 다양한 교재를 포함하여 현재 발행물은 이를 위해 Test Suite라는 용어를 사용합니다.
googletest에서 사용되는 테스트라는 용어는 ISTQB 및 기타 Test Case라는 용어에 해당합니다.
Test라는 용어는 ISTQB의 Test Case 정의를 포함하여 일반적으로 충분히 의미가 있으므로 여기서 큰 문제는 아닙니다. 그러나 Google 테스트에 사용 된 Test Cse라는 용어는 모순되는 의미이므로 혼동됩니다.
googletest는 최근 Test Case라는 용어를 Test Suite로 대체하기 시작했습니다. 선호하는 API는 TestSuite입니다. 이전 TestCase API는 천천히 사용이 중단되고 리팩터링됩니다
따라서 용어에 대한 다른 정의에 유의하십시오.
Assertions
googletest의 assertions은 함수 호출과 유사한 매크로입니다. 동작에 대해 assertions을 작성하여 클래스 또는 함수를 테스트합니다. assertions이 실패하면 googletest는 assertions의 소스 파일과 줄 번호 위치를 실패 메시지와 함께 인쇄합니다. googletest의 메시지에 추가 될 맞춤 실패 메시지를 제공 할 수도 있습니다.assertions은 쌍을 이루어 동일한 것을 테스트하지만 현재 기능에 다른 영향을 미칩니다. 따라서
ASSERT_*은 실패하면 치명적 실패를 생성하고 현재 기능을 중단합니다.
EXPECT_*은 치명적이지 않은 오류를 생성하여 현재 기능을 중단시키지 않습니다. 일반적으로 EXPECT_* 는 테스트에서 둘 이상의 실패를보고 할 수 있으므로 선호됩니다. 그러나 해당 assertions이 실패 할 때 계속하는 것이 타당하지 않은 경우 ASSERT_ *를 사용해야합니다.
실패한 ASSERT_*는 현재 함수에서 즉시 리턴하므로 그 뒤에 오는 clean-up코드를 건너 뛸 수 있으므로 space leak(or memory leak)이 발생할 수 있습니다. 누출의 특성에 따라 수정해야 할 수도 있고 그렇지 않을 수도 있습니다. 따라서 어설 션 오류 외에 힙 검사기 오류가 발생하면이 점을 명심하십시오.
사용자 정의 실패 메시지를 제공하려면 << 연산자를 사용하여 매크로에 간단히 메시지를 스트리밍하십시오. 예를 들면 :
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; for (int i = 0; i < x.size(); ++i) { EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; }
Basic Assertions
이러한 assertions은 참/거짓 조건 테스트를 수행합니다.
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_TRUE(condition); | EXPECT_TRUE(condition); | condition is true |
ASSERT_FALSE(condition); | EXPECT_FALSE(condition); | condition is false |
실패하면 ASSERT_ *는 치명적 실패를 생성하고 현재 함수에서 리턴하며 EXPECT_ *는 치명적이지 않은 실패를 생성하여 함수를 계속 실행할 수 있습니다. 두 경우 모두 assertions 오류는 테스트에 실패했음을 의미합니다.
Binary Comparison
이 섹션에서는 두 값을 비교하는 assertions에 대해 설명합니다.
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_EQ(val1, val2); | EXPECT_EQ(val1, val2); | val1 == val2 |
ASSERT_NE(val1, val2); | EXPECT_NE(val1, val2); | val1 != val2 |
ASSERT_LT(val1, val2); | EXPECT_LT(val1, val2); | val1 < val2 |
ASSERT_LE(val1, val2); | EXPECT_LE(val1, val2); | val1 <= val2 |
ASSERT_GT(val1, val2); | EXPECT_GT(val1, val2); | val1 > val2 |
ASSERT_GE(val1, val2); | EXPECT_GE(val1, val2); | val1 >= val2 |
이러한 assertions은 사용자 정의 형식에서 작동하지만 해당 비교 연산자 (예 : == 또는 <)를 정의한 경우에만 작동합니다. Google C++ 스타일 가이드에서는 권장하지 않으므로 ASSERT_TRUE() 또는 EXPECT_TRUE()를 사용하여 사용자 정의 유형의 두 객체가 동일한 지 확인해야합니다.
그러나 ASSERT_EQ (실제, 예상)가 ASSERT_TRUE (실제 == 예상)보다 선호됩니다. 실패시 실제 및 예상 값을 알려줍니다.
인수는 항상 정확히 한 번만 평가됩니다. 따라서 인수에 side effects를 가진것도 좋습니다. 그러나 일반적인 C/C++ 함수와 마찬가지로 인수의 평가 순서는 정의되어 있지 않으며 (즉, 컴파일러는 순서를 자유롭게 선택할 수 있습니다) 코드는 특정 인수 평가 순서에 의존해서는 안됩니다.
ASSERT_EQ()는 포인터에서 포인터 동등성을 수행합니다. 두 개의 C 문자열에서 사용되는 경우 동일한 값이 아닌 동일한 메모리 위치에 있는지 테스트합니다. 따라서 C 문자열 (예 : const char *)을 값으로 비교하려면 나중에 설명 할 ASSERT_STREQ()를 사용하십시오. 특히 C 문자열이 NULL임을 확인하려면 ASSERT_STREQ(c_string, NULL)를 사용하십시오. c ++ 11이 지원되는 경우 ASSERT_EQ(c_string, nullptr)를 사용하십시오. 두 개의 문자열 객체를 비교하려면 ASSERT_EQ를 사용해야합니다.
포인터 비교를 수행 할 때 *_EQ(ptr, NULL) 및 *_NE(ptr, NULL) 대신 *_EQ(ptr, nullptr) 및 *_NE(ptr, nullptr)을 사용하십시오.자세한 내용은 FAQ를 참조하십시오.
String Comparison
이 그룹의 assertions은 두 개의 C문자열을 비교합니다. 두 개의 문자열 객체를 비교하려면 EXPECT_EQ, EXPECT_NE 등을 대신 사용하십시오.Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_STREQ(str1,str2); | EXPECT_STREQ(str1,str2); | 두 문자열의 내용이 동일합니다 |
ASSERT_STRNE(str1,str2); | EXPECT_STRNE(str1,str2); | 두 문자열의 내용이 다릅니다 |
ASSERT_STRCASEEQ(str1,str2); | EXPECT_STRCASEEQ(str1,str2); | 두 문자열은 대/소 문자를 무시하고 내용이 동일합니다. |
ASSERT_STRCASENE(str1,str2); | EXPECT_STRCASENE(str1,str2); | 두 문자열은 대소 문자를 무시하고 내용이 다릅니다. |
Simple Tests
테스트를 만들려면1. 테스트 함수를 정의하고 이름을 지정하려면 TEST() 매크로를 사용하십시오. 이들은 값을 반환하지 않는 일반적인 C++ 함수입니다.
2. 이 함수에서 포함하려는 유효한 C++ 문과 함께 다양한 googletest 어설 션을 사용하여 값을 확인하십시오.
3. 테스트 결과는 assertions에 의해 결정됩니다. 테스트에서 어설 션이 치명적이거나 치명적이지 않은 경우 또는 테스트가 중단되면 전체 테스트가 실패합니다. 그렇지 않으면 성공합니다.
TEST(TestSuiteName, TestName) {
... test body ...
}
TEST() 인수는 일반에서 구체적으로 진행됩니다. 첫 번째 인수는 test suite의 이름이고 두 번째 인수는 test suite내의 테스트 이름입니다. 두 이름 모두 유효한 C++ 식별자 여야하며 밑줄 (_)을 포함해서는 안됩니다. 테스트의 전체 이름은 포함된 test suite와 개별 이름으로 구성됩니다. 다른 테스트 test suite의 테스트는 동일한 개별 이름을 가질 수 있습니다.예를 들어 간단한 정수 함수를 보자.
int Factorial(int n); // Returns the factorial of n이 함수의 test suite는 다음과 같습니다.
// Tests factorial of 0. TEST(FactorialTest, HandlesZeroInput) { EXPECT_EQ(Factorial(0), 1); } // Tests factorial of positive numbers. TEST(FactorialTest, HandlesPositiveInput) { EXPECT_EQ(Factorial(1), 1); EXPECT_EQ(Factorial(2), 2); EXPECT_EQ(Factorial(3), 6); EXPECT_EQ(Factorial(8), 40320); }googletest는 테스트 결과를 test suite별로 그룹화하므로 논리적으로 관련된 테스트는 동일한 test suite에 있어야합니다. 다시 말해, TEST()에 대한 첫 번째 인수는 동일해야합니다. 위의 예에서, 동일한 test suite인 FactorialTest에 속하는 두 개의 테스트 (HandlesZeroInput 및 HandlesPositiveInput)가 있습니다.
테스트 test suite이름을 지정할 때는 함수 및 클래스 이름 지정과 동일한 규칙을 따라야합니다.
Test Fixtures: Using the Same Data Configuration for Multiple Tests {#same-data-multiple-tests}
비슷한 데이터에서 작동하는 둘 이상의 테스트를 작성하는 경우 테스트 픽스처를 사용할 수 있습니다. 이를 통해 여러 가지 다른 테스트에 동일한 구성의 객체를 재사용 할 수 있습니다.
fixture을 만들려면 :
1. :: testing :: Test에서 클래스를 파생시킵니다. 서브 클래스에서 fixture 멤버에 접근하고 싶기 때문에 protected :로 본문을 시작하십시오.
2. 클래스 내에서 사용할 객체를 선언하십시오.
3. 필요한 경우 기본 생성자 또는 SetUp() 함수를 작성하여 각 테스트에 대한 개체를 준비하십시오. 일반적인 실수는 C++ 11에서 대문자 U를 소문자 u로 재정의 하여 SetUp()을 Setup()으로 사용하는 것 입니다. 철자가 올바른지 확인하세요.
4. 필요한 경우, 소멸자 또는 TearDown() 함수를 작성하여 SetUp()에 할당 한 모든 리소스를 해제하십시오. 생성자/소멸자를 사용해야 할 때와 SetUp()/TearDown()을 사용해야 할 때를 알려면 FAQ를 읽으십시오.
5. 필요한 경우 테스트에서 공유 할 서브 루틴을 정의하십시오.
TEST_F(TestFixtureName, TestName) {
... test body ...
}
TEST()와 마찬가지로 첫 번째 인수는 test suite이름이지만 TEST_F()의 경우 test fixture클래스의 이름이어야합니다. 당신은 아마 추측했을 것입니다 : _F는 Fixture입니다.
불행히도 C++ 매크로 시스템은 두 가지 유형의 테스트를 모두 처리 할 수있는 단일 매크로를 만들 수 없습니다. 잘못된 매크로를 사용하면 컴파일러 오류가 발생합니다.
또한 test fixture클래스를 TEST_F()에서 사용하기 전에 먼저 정의해야합니다. 그렇지 않으면 컴파일러 오류 "virtual outside class declaration"이 발생합니다.
TEST_F()로 정의 된 각 테스트에 대해 googletest는 런타임시 새로운 테스트 설비를 생성하고 즉시 SetUp()을 통해 초기화하고 테스트를 실행 한 다음 TearDown()을 호출하여 정리 한 다음 테스트 설비를 삭제합니다. 동일한 test suite의 테스트마다 test fixture객체가 다르므로 googletest는 다음 test fixture를 작성하기 전에 항상 test fixture를 삭제합니다. googletest는 여러 테스트에 동일한 test fixture를 재사용하지 않습니다. 한 번의 테스트로 texture를 변경해도 다른 테스트에는 영향을 미치지 않습니다.
예를 들어, 다음 인터페이스가있는 Queue라는 FIFO 대기열 클래스에 대한 테스트를 작성해 보겠습니다.
template <typename E> // E is the element type. class Queue { public: Queue(); void Enqueue(const E& element); E* Dequeue(); // Returns NULL if the queue is empty. size_t size() const; ... };
먼저 fixture클래스를 정의하십시오. 규칙에 따라 FooTest라는 이름을 지정해야합니다. 여기서 Foo는 테스트중인 클래스입니다.
class QueueTest : public ::testing::Test { protected: void SetUp() override { q1_.Enqueue(1); q2_.Enqueue(2); q2_.Enqueue(3); } // void TearDown() override {} Queue<int> q0_; Queue<int> q1_; Queue<int> q2_; };
이 경우 소멸자가 이미 수행 한 것 이외의 각 테스트 후에 정리할 필요가 없으므로 TearDown()이 필요하지 않습니다.
이제 TEST_F ()와이 조명기를 사용하여 테스트를 작성합니다.
TEST_F(QueueTest, IsEmptyInitially) { EXPECT_EQ(q0_.size(), 0); } TEST_F(QueueTest, DequeueWorks) { int* n = q0_.Dequeue(); EXPECT_EQ(n, nullptr); n = q1_.Dequeue(); ASSERT_NE(n, nullptr); EXPECT_EQ(*n, 1); EXPECT_EQ(q1_.size(), 0); delete n; n = q2_.Dequeue(); ASSERT_NE(n, nullptr); EXPECT_EQ(*n, 2); EXPECT_EQ(q2_.size(), 1); delete n; }
위의 ASSERT_* 및 EXPECT_* 어설 션을 모두 사용합니다. 경험적으로 어설 션 오류 후에 테스트에서 더 많은 오류를 계속 나타내려면 EXPECT_*를 사용하고 실패 후 계속할 때는 ASSERT_*를 사용하는 것이 좋습니다. 예를 들어, Dequeue 테스트에서 두 번째 어설 션은 ASSERT_NE(nullptr, n)입니다. 나중에 포인터 n을 역 참조해야하므로 n이 NULL 일 때 segfault가 발생합니다.
이러한 테스트가 실행되면 다음이 발생합니다.
1. googletest는 QueueTest 객체를 생성합니다 (t1이라고 함).
2. t1.SetUp ()은 t1을 초기화합니다.
3. 첫 번째 테스트 (IsEmptyInitially)는 t1에서 실행됩니다.
4. 테스트가 완료된 후 t1.TearDown ()이 정리됩니다.
5. t1이 파괴되었습니다.
6. 위의 단계는 다른 QueueTest 객체에서 반복되며 이번에는 DequeueWorks 테스트를 실행합니다.
테스트 호출
TEST() 및 TEST_F()는 테스트를 googletest에 암시 적으로 등록합니다. 따라서 다른 많은 C++ 테스트 프레임 워크와 달리 정의 된 테스트를 모두 실행하기 위해 다시 나열 할 필요는 없습니다.테스트를 정의한 후 RUN_ALL_TESTS()로 테스트를 실행할 수 있습니다. 모든 테스트가 성공하면 0을, 그렇지 않으면 1을 리턴합니다. RUN_ALL_TESTS ()는 링크 단위에서 모든 테스트를 실행합니다. 테스트 단위 나 소스 파일이 다를 수 있습니다.
호출되면
RUN_ALL_TESTS()
매크로는 다음과 같습니다.- 모든 googletest 플래그의 상태를 저장합니다.
- 첫 번째 테스트에 대한 테스트 픽스처 객체를 만듭니다.
- SetUp ()을 통해 초기화합니다.
- fixture 객체에서 테스트를 실행합니다.
- TearDown ()을 통해 조명기를 정리합니다.
- fixture를 삭제합니다.
- 모든 googletest 플래그의 상태를 복원합니다.
- 모든 테스트가 실행될 때까지 다음 테스트에 대해 위 단계를 반복합니다.
중요 : RUN_ALL_TESTS()의 반환 값을 무시해서는 안됩니다. 그렇지 않으면 컴파일러 오류가 발생합니다. 이 설계의 근거는 자동화 된 테스트 서비스가 stdout/stderr 출력이 아니라 종료 코드를 기반으로 테스트가 통과했는지 여부를 결정한다는 것입니다. 따라서 main() 함수는 RUN_ALL_TESTS()의 값을 반환해야합니다.
또한 RUN_ALL_TESTS()를 한 번만 호출해야합니다. 그것을 두 번 이상 호출하면 일부 고급 Google 테스트 기능 (예 : 스레드 안전 사망 테스트)과 충돌하므로 지원되지 않습니다.
Writing the main() Function
대부분의 사용자는 고유한 기본 기능을 작성할 필요가 없으며 대신 적절한 진입 점을 정의하는 gtest_main(gtest와 반대로)과 연결해야합니다. 자세한 내용은이 섹션의 끝 부분을 참조하십시오. 이 섹션의 나머지 부분은 테스트를 실행하기 전에 fixture 및test suites의 프레임 워크 내에서 표현할 수없는 사용자 정의 작업을 수행해야하는 경우에만 적용해야합니다.자체 main() 함수를 작성하면 RUN_ALL_TESTS() 값을 리턴해야합니다.
이 상용구에서 시작할 수 있습니다 :
#include "this/package/foo.h" #include "gtest/gtest.h" namespace my { namespace project { namespace { // The fixture for testing class Foo. class FooTest : public ::testing::Test { protected: // You can remove any or all of the following functions if their bodies would // be empty. FooTest() { // You can do set-up work for each test here. } ~FooTest() override { // You can do clean-up work that doesn't throw exceptions here. } // If the constructor and destructor are not enough for setting up // and cleaning up each test, you can define the following methods: void SetUp() override { // Code here will be called immediately after the constructor (right // before each test). } void TearDown() override { // Code here will be called immediately after each test (right // before the destructor). } // Class members declared here can be used by all tests in the test suite // for Foo. }; // Tests that the Foo::Bar() method does Abc. TEST_F(FooTest, MethodBarDoesAbc) { const std::string input_filepath = "this/package/testdata/myinputfile.dat"; const std::string output_filepath = "this/package/testdata/myoutputfile.dat"; Foo f; EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0); } // Tests that Foo does Xyz. TEST_F(FooTest, DoesXyz) { // Exercises the Xyz feature of Foo. } } // namespace } // namespace project } // namespace my int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
:: testing :: InitGoogleTest() 함수는 googletest 플래그에 대한 명령 행을 구문 분석하고 인식 된 모든 플래그를 제거합니다. 이를 통해 사용자는 AdvancedGuide에서 다룰 다양한 플래그를 통해 테스트 프로그램의 동작을 제어 할 수 있습니다. RUN_ALL_TESTS()를 호출하기 전에 이 함수를 호출해야합니다. 그렇지 않으면 플래그가 제대로 초기화되지 않습니다.
Windows에서는 InitGoogleTest()도 넓은 문자열과 함께 작동하므로 UNICODE 모드로 컴파일 된 프로그램에서도 사용할 수 있습니다.
그러나 모든 주요 기능을 작성하는 것이 너무 많은 일이라고 생각하십니까? 우리는 전적으로 귀하에게 동의하며, 이것이 Google 테스트가 main()의 기본 구현을 제공하는 이유입니다. 요구 사항에 맞는 경우 테스트를 gtest_main 라이브러리와 연결하면 좋습니다.
참고 : ParseGUnitFlags ()는 InitGoogleTest()를 위해 더 이상 사용되지 않습니다.
댓글 없음:
댓글 쓰기