เริ่มหัดเขียน unit test C++ ด้วย Google Test
เมื่อวันก่อนนัดกับพี่ตู่และกลุ่ม clockup studio เพื่อเรียนรู้เรื่อง unit test สำหรับงานเกมด้วย C++ กัน แต่เสียดายงานเข้าเลยไม่ได้ฟังทั้งหมด T_T แต่ก็ยังดีที่ได้ไปตอนที่พี่ตู่พาทำ setup และ run test พอดีเลยได้กลับมาศึกษาเพิ่มเติมและสรุปในนี้เลยดีกว่า
เครื่องมือที่ใช้มีอะไรบ้าง
- C/C++ compiler ผมใช้ clang ที่มากับ command line tools ของ macOS หรือจะใช้ gcc ก็ได้ไม่ว่ากัน
- cmake สำหรับ build sources code
- git สำหรับ clone
เริ่มขั้นตอนแรกติดตั้ง Google Test
ด้วยการ clone source code จาก https://github.com/google/googletest
$ git clone https://github.com/google/googletest.git
จากนั้นทำการ compile ตัว Google Test ด้วย CMake
$ cd googletest
$ mkdir build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make
$ make install # ถ้าติด permission ให้ใช้ sudo นะ
สร้างเทสไฟล์
ผมสร้างไฟล์ชื่อ FizzBuzzTest.cc
และเขียนดังนี้
#include <gtest/gtest.h>TEST(FizzBuzzTest, Fizz) {
}int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
เทสของ Google Test จะประกอบไปด้วยสองส่วนคือ
- test function ตัว Google Test จะใช้ macro ชื่อว่า
TEST
ที่รับชื่อ test suite กับ test case และมี {} สำหรับ detail ของ test case นั้นๆ - main function เนื่องจากการ run test ใน C/C++ เราจะต้อง compile เป็น binary ก่อนเสมอ ข้างในจะต้องเรียก
InitGoogleTest
เพื่อทำให้ Google Test parse flags ได้ และเรียกRUN_ALL_TESTS
เพื่อรันการทดสอบ (RUN_ALL_TESTS
เป็น macro นะครับ)
Compile เทสและรัน
โดย
$ clang++ -I/usr/local/include -L/usr/local/lib -lgtest FizzBuzzTest.cc -o FizzBuzzTest
$ ./FizzBuzzTest
ผลลัพธ์ที่ได้จากการรันเทสคือ
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from FizzBuzzTest
[ RUN ] FizzBuzzTest.Fizz
[ OK ] FizzBuzzTest.Fizz (0 ms)
[----------] 1 test from FizzBuzzTest (0 ms total)[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
เริ่มต้นการเขียนเทส
เริ่มเขียนเทสใน TEST(FizzBuzzTest, Fizz)
โดย
TEST(FizzBuzzTest, Fizz) {
EXPECT_EQ("Fizz", fizzbuzz::Say(3).c_str());
}
ใช้ EXPECT_EQ
สำหรับเปรียบเทียบผลลัพธ์ ซึ่ง parameter ตัวแรกคือผลลัพธ์ที่คาดหวัง (expect) และ output ที่ได้จาก function ที่เราทดสอบ (actual)
จากนั้นทำการ compile และ run เพื่อดูผล ซึ่งจะต้อง compile ไม่ผ่านนะ เพราะเรายังไม่ได้เริ่ม implement เลย
Implement code ให้เร็วที่สุด
โดยผมสร้่าง function ไว้ภายในไฟล์เทส เพื่อให้เห็นภาพว่ามันสามารถเรียกได้จริง
namespace fizzbuzz {
std::string Say(const int n) {
return "Fizz";
}
}
จากนั้นทำการ compile และ run อีกรอบผลที่ได้ควรจะรันผ่าน
เริ่ม move code ไปอยู่อีกไฟล์
จริงๆ แล้วโค้ดของเราไม่ควรจะอยู่ในไฟล์เทส เพราะฉะนั้นผมจะสร้างไฟล์ชื่อ FizzBuzz.cc
เพื่อเก็บ function ของเราและ FizzBuzz.h
สำหรับ header ของ function
// in FizzBuzz.h#ifndef FIZZBUZZ_H
#define FIZZBUZZ_Hnamespace fizzbuzz {
std::string Say(const int);
}#endif // end FIZZBUZZ_H// in FizzBuzz.cc
#include <iostream>#include "FizzBuzz.h"std::string fizzbuzz::Say(const int n) {
return "Fizz";
}
จากนั้นทำการแก้ไขวิธีการ compile โดยเพิ่มไฟล์ FizzBuzz.cc
เข้าไปตอน compile ด้วย
$ clang++ -L/usr/local/lib -I/usr/local/include -lgtest FizzBuzz.cc FizzBuzzTest.cc -o FizzBuzzTest
$ ./FizzBuzzTest
เท่านี้ก็เริ่มต้นเขียนเทสต่อๆ ไปได้แล้ว :)