운영체제 lab 개발하면서 google test framework를 처음으로 사용해보았습니다. 기본개념 및 사용법을 기억하기 위해 기록합니다!!
Google Test framework
: 호스트 PC 환경 혹은 서버 개발 환경에서 실제와 비슷하게 동작할 수 있는 테스트 환경( H/W를 mocking을 통해 S/W만으로 테스트 가능 )
::testing( ::gtest ) : Google Test 프레임워크에서 사용되는 네임스페이스(namespace), 테스트 관련 기능들이 구현( TEST를 비롯한 매크로 & 함수 )
- ASSERT_EQ(A, B); - Fail되는 순간 테스트가 멈춤
- EXPECT_EQ(A, B); - 결과 값 Pass/Fail 여부에 상관없이 계속 진행하고, 마지막에 Fail 된 테스트를 알려준다.
for (int i = 0; i < sizeof(x); ++i) {
EXPECT_EQ(x[i], y[i]);
}
기본개념 및 구조
Test Case : 개별적인 기능, 동작을 테스트
- TEST() : 하나의 함수를 테스트 → TEST( 소속되는 suite, 개별 case이름 )
Test Suite : 하나 이상의 테스트 케이스를 포함
/*
TEST(TestSuiteName, TestName) {
...
}
*/
// 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);
}
Test Fixture : 실행 전후에 필요한 환경을 설정 → 여러 테스트 케이스에서 공통된 데이터(공유 데이터 및 객체 )나 동작이 필요할 경우에 활용
(1) Google Test 프레임워크에서 제공하는 기본 테스트 케이스 클래스(::testing::Test) 상속하여 클래스 생성 → SetUp과 TearDown을 오버라이딩
- SetUp() : 테스트 케이스가 실행되기 전에 호출되는 함수 / 사전에 준비된 데이터 세팅, 초기화 → 동일한 상태에서 테스트
- TearDown() : 테스트 케이스가 실행된 후에 호출되는 함수 / 하나의 테스트가 끝나면 기존의 상태로 원복, 불필요한 데이터를 제거 → 독립적인 테스트 구성
class QueueTest : public ::testing::Test {
protected:
void SetUp() override {
// q0_ remains empty
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// void TearDown() override {} -> 불필요하여 구현하지 않음
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
(2) 테스트 픽스처의 실제 적용 및 구현
: TEST_F( 구현한 테스트 픽스쳐, 테스트케이스 이름 )
/*
TEST_F(TestFixtureName, TestName) {
... test body ...
}
*/
//IsEmptyInitially : q0이 비어있는지 확인하는 테스
TEST_F(QueueTest, IsEmptyInitially) {
EXPECT_EQ(q0_.size(), 0);
}
//DequeueWorks : Deque가 동작하는지 확인하는 테스트
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;
}
테스트케이스의 파라미터화로 활용
동작방식 : INSTANTIATE_TEST_CASE_P 매크로의 MyTestFixture와 TEST_P 매크로의 TestCaseName가 일치할때 인스턴스화하는 대상으로 인식한다.
[ main ] - 테스트를 실행하는 소스파일
::testing::InitGoogleTest(); 구글 테스트 프레임워크 초기화
RUN_ALL_TESTS() : 등록된 모든 테스트를 실행
[ test.cpp ] - 테스트환경을 구축하는 소스파일
INSTANTIATE_TEST_CASE_P() : 파라미터화된 테스트 케이스를 인스턴스화하는 매크로( 생성 & 데이터 전달 ) -> 특정한 로직의 테스트 케이스를 다양한 입력 값과 함께 여러 번 실행가능
INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator_function);
- prefix : 인스턴스 이름의 접두사(prefix) / 인스턴스의 자동생성 이름앞에 붙게됨
- test_case_name : 테스트 케이스 이름, 미리 정의된 테스트 케이스를 가리키는 포인터
- generator_function : 파라미터 값을 생성하는 함수, 각 테스트 케이스에 전달될 파라미터를 생성
=> 이로써 ::testing::Values에서 정의된 워크로드 수 * TEST_P()에서 정의된 테스트케이스 수 만큼의 테스트가 수행된다!!!
(1) ::testing::Values(Param1, Param2, …): 각 인자값들을 가지고 각기 다른 테스트 케이스 인스턴스를 생성하는 매크로( 인자값만큼의 테스트케이스를 생성 )
다양한 종류의 워크로드를 가지고 정의된 테스트를 실행해볼 수 있다.
::testing::Values(
// std::make_tuple("워크로드 파일", "context_switch 시간"),
std::make_tuple("A", 0.01),
std::make_tuple("A", 0.1),
std::make_tuple("B", 0.05),
std::make_tuple("B", 0.2)
)
⇒ 각 테스트 케이스에 워크로드파일 & context_switch 시간 정보를 전달
(2) TEST_P() : 파라미터화된 테스트 케이스를 정의
TEST_P(TestCaseName, TestName) {
// 테스트 로직
}
- TestCaseName: 테스트 케이스의 이름 / 파라미터화된 테스트 케이스 클래스(상속받게 정의한 클래스)의 이름과 동일
- TestName: 테스트 함수의 이름 / 파라미터화된 테스트 케이스 클래스 안에서 유일
[ test_util.cpp ] - 테스트 내용을 정의하는 소스파일
class XXXXtest : TestWithParam(::testing::TestWithParam<std::tuple<자료형, 자료형>>)을 상속받는 사용자 정의 테스트 클래스
- 여러 파라미터 값( <std::tuple<자료형, 자료형>> )에 대해 동일한 테스트를 반복해서 수행가능 ↔ testng::Test상속은 단일값만 테스트
- 테스트 클래스를 여러개 구축하고 TEST_P()에서 testcase name으로 각각 입력가능
** 해당 상속클래스의 구성 **
(1) 테스트에 이용되는 각종 멤버변수
(2) 테스트에 이용되는 각종 멤버함수
(3) SetUp() 오버라이딩
(4) TearDown() 오버라이딩
'dev tech' 카테고리의 다른 글
[C++] 디자인패턴 RAII를 이용한 자원관리 (1) | 2024.06.11 |
---|---|
Make 및 Makefile의 개념 및 사용 (0) | 2024.05.20 |
WSL(Windows Subsystem for Linux)의 개념과 기본명령 (0) | 2024.05.20 |
형상관리 Git 사용법 (0) | 2024.03.10 |