CSV 파싱 로직과 Google Test 예제
1. 프로젝트 구조
이전 예제들과 동일하게 Windows + Cygwin + VSCode 환경과 CMake 기반 프로젝트를 가정하며, 디렉터리 구조는 아래와 같습니다.
project-root/
├── src/
│ ├── csv_parser.h
│ └── csv_parser.cpp
├── test/
│ ├── test_csv_parser.cpp
│ └── test_main.cpp
└── CMakeLists.txt
- src 디렉터리에 CSV 파싱 관련 로직(CSVParser 라이브러리)을 구현합니다.
- test 디렉터리에 Google Test를 이용한 테스트 코드를 작성합니다.
- 최상위 CMakeLists.txt에서 프로젝트 및 Googletest 설정을 하고, src/CMakeLists.txt와 test/CMakeLists.txt를 각각 추가한다고 가정합니다
2. CSV 파서 구현
이번 예제는 parseCSVLine 함수를 중심으로, 한 줄의 CSV 데이터만 파싱한다고 가정합니다.
- 쉼표(,)로 구분된 필드를 추출.
- 따옴표(")로 감싸인 필드는 내부의 쉼표를 그대로 데이터로 해석.
- 따옴표 안에 이스케이프를 다루는 등 복잡한 기능까지는 생략(기본적인 기능만).
2.1 src/csv_parser.h
#ifndef CSV_PARSER_H
#define CSV_PARSER_H
#include <string>
#include <vector>
/*
간단 CSV 한 줄 파싱 함수.
예) parseCSVLine("Gildong,Hong,30")
-> {"Gildong", "Hong", "30"}
예) parseCSVLine("\"Hong, Gildong\",25")
-> {"Hong, Gildong", "25"}
예) parseCSVLine(" one , two , \" three \" ")
-> {" one ", " two ", " three "}
*/
std::vector<std::string> parseCSVLine(const std::string& line);
#endif // CSV_PARSER_H
2.2 src/csv_parser.cpp
#include "csv_parser.h"
#include <sstream>
/*
간단 CSV 파싱 규칙:
1) 필드 구분자는 쉼표 (,).
2) 필드가 큰따옴표(")로 시작하면, 동일 큰따옴표(")가 나올 때까지를 한 필드로 간주.
3) 내부에 쉼표가 있어도 큰따옴표 안에서는 일반 문자로 처리.
4) 큰따옴표 안에 이스케이프 처리 등은 생략(실무에선 더 복잡한 처리가 필요).
*/
std::vector<std::string> parseCSVLine(const std::string& line) {
std::vector<std::string> fields;
std::string current;
bool inQuotes = false;
for (size_t i = 0; i < line.size(); ++i) {
char c = line[i];
// 큰따옴표 처리
if (c == '"') {
// inQuotes 토글
inQuotes = !inQuotes;
}
else if (c == ',' && !inQuotes) {
// 쉼표이면서 큰따옴표 바깥이면 필드 종료
fields.push_back(current);
current.clear();
}
else {
// 일반 문자 추가
current.push_back(c);
}
}
// 마지막 필드
fields.push_back(current);
return fields;
}
팁:
실제 CSV 파서는 따옴표 내의 이스케이프(\") 등도 처리해야 하고, 줄 끝의 개행 처리도 필요합니다. 여기서는 예제이므로 단순화했습니다.
3. 테스트 코드
3.1 test/test_main.cpp
#include <gtest/gtest.h>
// Google Test의 메인 함수 정의
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
3.2 test/test_csv_parser.cpp
#include <gtest/gtest.h>
#include "csv_parser.h"
/*
TEST(테스트스위트이름, 테스트이름)을 이용해
다양한 CSV 파싱 시나리오를 단일 테스트 단위로 구성.
*/
TEST(CSVParserTest, SimpleLine) {
// 쉼표로 단순 구분된 경우
std::string line = "Gildong,Hong,30";
auto fields = parseCSVLine(line);
ASSERT_EQ(fields.size(), 3u); // 필드는 3개
EXPECT_EQ(fields[0], "Gildong");
EXPECT_EQ(fields[1], "Hong");
EXPECT_EQ(fields[2], "30");
}
TEST(CSVParserTest, QuotedField) {
// 따옴표로 감싸진 필드 안의 쉼표는 필드 구분자가 아님
std::string line = "\"Hong, Gildong\",25";
auto fields = parseCSVLine(line);
ASSERT_EQ(fields.size(), 2u);
EXPECT_EQ(fields[0], "Hong, Gildong");
EXPECT_EQ(fields[1], "25");
}
TEST(CSVParserTest, MixedSpaces) {
// 필드 내의 공백 유지
std::string line = " one , two , \" three \" ";
auto fields = parseCSVLine(line);
ASSERT_EQ(fields.size(), 3u);
// 공백 포함 여부 확인
EXPECT_EQ(fields[0], " one ");
EXPECT_EQ(fields[1], " two ");
EXPECT_EQ(fields[2], " three ");
}
TEST(CSVParserTest, EmptyFields) {
// 쉼표 연속으로 등장하면 빈 필드
std::string line = "Gildong,,Hong";
auto fields = parseCSVLine(line);
ASSERT_EQ(fields.size(), 3u);
EXPECT_EQ(fields[0], "Gildong");
EXPECT_EQ(fields[1], ""); // 중간 필드가 비어있음
EXPECT_EQ(fields[2], "Hong");
}
TEST(CSVParserTest, AllQuotedFields) {
// 모든 필드가 따옴표로 감싸진 경우
std::string line = "\"Apple\",\"Banana\",\"Cherry\"";
auto fields = parseCSVLine(line);
ASSERT_EQ(fields.size(), 3u);
EXPECT_EQ(fields[0], "Apple");
EXPECT_EQ(fields[1], "Banana");
EXPECT_EQ(fields[2], "Cherry");
}
TEST(CSVParserTest, UnbalancedQuotes) {
// 실수로 따옴표가 닫히지 않은 경우 -> 현재 로직에서는 단순히 끝까지 한 필드로 처리
std::string line = "\"Data, still in quotes,No closing quote";
auto fields = parseCSVLine(line);
// 실제 상황에선 이런 경우 오류 처리를 할 수도 있음
ASSERT_EQ(fields.size(), 1u);
EXPECT_EQ(fields[0], "Data, still in quotes,No closing quote");
}
설명:
TEST(CSVParserTest, SimpleLine): 가장 기본적인 쉼표 구분 시나리오.
TEST(CSVParserTest, QuotedField): 따옴표로 감싸진 필드 내 쉼표 처리 확인.
TEST(CSVParserTest, MixedSpaces): 공백 유무, 따옴표가 포함된 필드.
TEST(CSVParserTest, EmptyFields): 연속된 쉼표로 인해 빈 필드가 생기는 케이스.
TEST(CSVParserTest, AllQuotedFields): 모든 필드가 따옴표로 감싸진 경우.
TEST(CSVParserTest, UnbalancedQuotes): 닫히지 않은 따옴표 상황(에러 처리할 수도 있지만, 여기선 간단히 한 필드로 처리).
4. 빌드 및 실행
- CMake 빌드
- 정상적으로 빌드되면 test_csv_parser 실행 파일이 생성됩니다.
$ cd project-root (개인 프로젝트 폴더로 이동)
$ mkdir build
$ cd build
$ cmake ..
$ make
- 테스트 실행
$ ./test_csv_parser
- Google Test가 위에서 정의한 6개 테스트 케이스를 모두 실행하고 결과를 콘솔에 출력합니다.
- 예시 출력:
5. 마무리
이 예제에서는 TEST() 매크로로 CSV 한 줄 파싱 기능을 여러 시나리오에 대해 검증해봤습니다. CSV 파일 파싱은 실무에서 파일 입출력, 데이터 처리 과정에 자주 등장하며, 주어진 예시는 다음과 같은 방면으로 확장할 수 있습니다.
- 파일 단위로 여러 줄을 읽어 파싱하는 기능 추가.
- 따옴표 안의 이스케이프(예: \") 처리, 멀티라인 따옴표 등 다양한 CSV 스펙 지원.
- 에러 처리(예: 따옴표 불일치 발생 시 예외 throw, 로그 남기기 등).
Google Test의 TEST()는 이렇게 기능 단위로 독립적인 테스트 케이스를 작성하기에 적합합니다. 실무에서 CSV 파서 외에도 로그 파서, 설정 파일 파서, JSON 파서 등의 텍스트 파싱 로직에 동일한 테스트 구조를 적용할 수 있으니 참고하시기 바랍니다.
'TDD 테스트주도개발 > TDD for C++' 카테고리의 다른 글
[GMock이해]ON_CALL vs EXPECT_CALL 차이 (0) | 2025.06.13 |
---|---|
[googletest문법6]TEST_F()확실하게 이해하자! (0) | 2025.04.09 |
[googletest문법4]파라메터 이용 TEST_P() 사용법 (1) | 2025.03.13 |
[googletest문법3]테스트 픽스처 TEST_F() 사용법 (0) | 2025.03.10 |
[Googletest문법2]단일테스트 TEST() 작성방법 (0) | 2025.03.08 |