테스트 작성하기
테스트 작성하기 (Writing Tests)
출처: 문서
본문
개발과 테스트 (Development and Testing)
새 기능이 추가되면 "행복한 경로(happy path)"뿐 아니라 엣지 케이스와 잘못된 사용법까지 테스트하는 올바른 테스트가 있는 게 매우 중요해요. 이 섹션에서는 DuckDB 테스트가 어떻게 구성되는지, DuckDB용 새 테스트를 어떻게 만드는지 설명할게요.
테스트는 test 폴더에 있는 unittest 프로그램으로 실행할 수 있어요. 기본 컴파일에서는 build/release/test/unittest(release) 또는 build/debug/test/unittest(debug)에 있어요.
철학 (Philosophy)
DuckDB를 테스트할 때 우리는 모든 테스트를 SQL로 라우팅하는 것을 목표로 해요. 컴포넌트를 개별적으로 테스트하는 것은 나중에 그 컴포넌트를 바꾸기 더 어렵게 만들기 때문에 피하려고 해요. 따라서 거의 모든 테스트가 순수 SQL로 표현될 수 있고(또는 그래야 하고), 특정 예외는 Catch Tests에서 다룰게요. 하지만 대부분의 경우 테스트를 일반 SQL로 작성해야 해요.
프레임워크 (Frameworks)
SQL 테스트는 sqllogictest 프레임워크로 작성해야 해요.
C++ 테스트는 Catch 프레임워크로 작성할 수 있어요.
클라이언트 커넥터 테스트 (Client Connector Tests)
DuckDB는 다양한 클라이언트 커넥터에 대한 테스트도 있어요. 이들은 보통 해당 클라이언트 언어로 작성되며 tools/*/tests에서 찾을 수 있어요. 또한 주어진 클라이언트에서 무엇이 가능해야 하는지에 대한 문서 역할도 해요.
테스트 데이터 생성 함수 (Functions for Generating Test Data)
DuckDB에는 테스트 데이터를 생성하는 내장 함수가 있어요.
test_all_types 함수
test_all_types 테이블 함수는 열이 타입(BOOL, TINYINT 등)에 해당하는 테이블을 생성해요. 이 테이블은 각 타입의 최솟값, 최댓값, NULL 값을 인코딩하는 세 행을 가져요.
FROM test_all_types();
┌─────────┬─────────┬──────────┬─────────────┬──────────────────────┬──────────────────────┬───┬──────────────────────┬──────────────────────┬──────────────────────┬──────────────────────┬──────────────────────┐
│ bool │ tinyint │ smallint │ int │ bigint │ hugeint │ … │ struct │ struct_of_arrays │ array_of_structs │ map │ union │
│ boolean │ int8 │ int16 │ int32 │ int64 │ int128 │ │ struct(a integer, … │ struct(a integer[]… │ struct(a integer, … │ map(varchar, varch… │ union("name" varch… │
├─────────┼─────────┼──────────┼─────────────┼──────────────────────┼──────────────────────┼───┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
│ false │ -128 │ -32768 │ -2147483648 │ -9223372036854775808 │ -17014118346046923… │ … │ {'a': NULL, 'b': N… │ {'a': NULL, 'b': N… │ [] │ {} │ Frank │
│ true │ 127 │ 32767 │ 2147483647 │ 9223372036854775807 │ 170141183460469231… │ … │ {'a': 42, 'b': 🦆… │ {'a': [42, 999, NU… │ [{'a': NULL, 'b': … │ {key1=🦆🦆🦆🦆🦆🦆… │ 5 │
│ NULL │ NULL │ NULL │ NULL │ NULL │ NULL │ … │ NULL │ NULL │ NULL │ NULL │ NULL │
├─────────┴─────────┴──────────┴─────────────┴──────────────────────┴──────────────────────┴───┴──────────────────────┴──────────────────────┴──────────────────────┴──────────────────────┴──────────────────────┤
│ 3 rows 44 columns (11 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
test_vector_types 함수
test_vector_types 테이블 함수는 _n_개의 인자 col1, ..., coln과 선택적 BOOLEAN 인자 all_flat을 받아요. 이 함수는 _n_개의 열 test_vector, test_vector2, ..., test_vectorn을 가진 테이블을 생성해요. 각 행에서 각 필드는 각자 열의 타입에 맞는 값을 담아요.
FROM test_vector_types(NULL::BIGINT);
┌──────────────────────┐
│ test_vector │
│ int64 │
├──────────────────────┤
│ -9223372036854775808 │
│ 9223372036854775807 │
│ NULL │
│ ... │
└──────────────────────┘
FROM test_vector_types(NULL::ROW(i INTEGER, j VARCHAR, k DOUBLE), NULL::TIMESTAMP);
┌──────────────────────────────────────────────────────────────────────┬──────────────────────────────┐
│ test_vector │ test_vector2 │
│ struct(i integer, j varchar, k double) │ timestamp │
├──────────────────────────────────────────────────────────────────────┼──────────────────────────────┤
│ {'i': -2147483648, 'j': 🦆🦆🦆🦆🦆🦆, 'k': -1.7976931348623157e+308} │ 290309-12-22 (BC) 00:00:00 │
│ {'i': 2147483647, 'j': goo\0se, 'k': 1.7976931348623157e+308} │ 294247-01-10 04:00:54.775806 │
│ {'i': NULL, 'j': NULL, 'k': NULL} │ NULL │
│ ... │
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘
test_vector_types에는 all_flat이라는 BOOL 타입의 선택적 인자가 있어요. 이는 벡터의 내부 표현에만 영향을 줘요.
FROM test_vector_types(NULL::ROW(i INTEGER, j VARCHAR, k DOUBLE), NULL::TIMESTAMP, all_flat = true);
-- 출력은 위와 같지만 내부 표현이 다름