โซลูชันส่วนหัวอย่างง่ายสำหรับการทดสอบหน่วยสำหรับ C/C ++
เพียงแค่ #include "utest.h" ในรหัสของคุณ!
แพลตฟอร์มที่รองรับปัจจุบันคือ Linux, MacOS และ Windows
คอมไพเลอร์ที่รองรับปัจจุบันคือ GCC, Clang, CL.EXE ของ MSVC และ Clang-cl.exe
นอกจากนี้ยังทำงานร่วมกับ TCC แต่มีข้อแม้: การเปิดตัวล่าสุดของ TCC Compiler (เวอร์ชัน 0.9.27) ไม่มีคุณสมบัติสำหรับ Utest ในการทำงาน ตรวจสอบให้แน่ใจว่าใช้ TCC ที่ได้รับการแก้ไขด้วยส่วนขยายแอตทริบิวต์ตัวสร้าง ล่าสุด Ubuntu และ Debian Linux Distros Ship TCC พร้อมแพทช์นั้นรวมอยู่แล้ว หากคุณรวบรวม TCC ด้วยตัวเองให้ใช้เวอร์ชัน Trunk และมันจะทำงานตามที่คาดไว้
utest.h รองรับตัวเลือกบรรทัดคำสั่งบางอย่าง:
--help เพื่อส่งข้อความช่วยเหลือ--filter=<filter> จะกรองกรณีทดสอบเพื่อเรียกใช้ (มีประโยชน์สำหรับการรันเคสทดสอบที่กระทำผิดโดยเฉพาะอีกครั้ง)--list-tests จะแสดงชื่อ TestNames หนึ่งต่อบรรทัด ชื่อเอาต์พุตสามารถส่งผ่านไป --filter--output=<output> จะส่งออกไฟล์ XUNIT XML พร้อมผลการทดสอบ (ที่ Jenkins, Travis-CI และ Appveyor สามารถแยกวิเคราะห์ผลการทดสอบได้)--enable-mixed-units จะช่วยให้เอาต์พุตต่อการทดสอบมีหน่วยผสม (S/MS/US/NS)--random-order[=<seed>] จะสุ่มลำดับที่การทดสอบจะรันหากไม่มีการโต้แย้งทางเลือกแล้วจะใช้เมล็ดเริ่มต้นแบบสุ่ม Utest เป็นไลบรารีส่วนหัวเดียวเพื่อเปิดใช้งานความสนุกทั้งหมดของการทดสอบหน่วยใน C และ C ++ ห้องสมุดได้รับการออกแบบมาเพื่อให้ผลลัพธ์คล้ายกับเฟรมเวิร์ก GoogleTest ของ Google:
[==========] Running 1 test cases.
[ RUN ] foo.bar
[ OK ] foo.bar (631ns)
[==========] 1 test cases ran.
[ PASSED ] 1 tests.
ในไฟล์ C หรือ C ++ หนึ่งไฟล์คุณต้องโทรหามาโคร utest_main:
UTEST_MAIN ()สิ่งนี้จะเรียกเข้า Utest.h สร้างอินสแตนซ์การทดสอบทั้งหมดและเรียกใช้เฟรมเวิร์กทดสอบหน่วย
อีกทางเลือกหนึ่งหากคุณต้องการเขียนหลักของคุณเองและโทรไปยัง utest.h คุณสามารถโทรหาไฟล์ C หรือ C ++ ได้แทน:
UTEST_STATE ();และเมื่อคุณพร้อมที่จะโทรเข้าสู่ Utest.h Framework ทำ:
int main ( int argc , const char * const argv []) {
// do your own thing
return utest_main ( argc , argv );
}ในการกำหนดกรณีทดสอบที่จะเรียกใช้คุณสามารถทำสิ่งต่อไปนี้
#include "utest.h"
UTEST ( foo , bar ) {
ASSERT_TRUE ( 1 );
}มาโคร Utest ใช้พารามิเตอร์สองตัว - ครั้งแรกคือชุดที่กรณีทดสอบเป็นของที่สองคือชื่อของการทดสอบ สิ่งนี้ช่วยให้การทดสอบถูกจัดกลุ่มเพื่อความสะดวก
testcase ที่ติดตั้งเป็นหนึ่งในที่มีโครงสร้างที่มีการสร้างอินสแตนซ์ที่สามารถแชร์ได้ในหลาย ๆ testcases
struct MyTestFixture {
char c ;
int i ;
float f ;
};
UTEST_F_SETUP ( MyTestFixture ) {
utest_fixture -> c = 'a' ;
utest_fixture -> i = 42 ;
utest_fixture -> f = 3.14f ;
// we can even assert and expect in setup!
ASSERT_EQ ( 42 , utest_fixture -> i );
EXPECT_TRUE (true);
}
UTEST_F_TEARDOWN ( MyTestFixture ) {
// and also assert and expect in teardown!
ASSERT_EQ ( 13 , utest_fixture -> i );
}
UTEST_F ( MyTestFixture , a ) {
utest_fixture -> i = 13 ;
// teardown will succeed because i is 13...
}
UTEST_F ( MyTestFixture , b ) {
utest_fixture -> i = 83 ;
// teardown will fail because i is not 13!
}บางสิ่งที่ควรทราบที่แสดงให้เห็นข้างต้น:
UTEST_F_SETUP และ UTEST_F_TEARDOWN แมโคร - แม้ว่าพวกเขาจะไม่ทำอะไรในร่างกายบางครั้งคุณต้องการใช้อุปกรณ์ติดตั้งเดียวกัน และ ทดสอบซ้ำ ๆ ซ้ำ ๆ แต่อาจเปลี่ยนตัวแปรหนึ่งตัวได้อย่างละเอียดภายใน นี่คือที่มาทดสอบที่จัดทำดัชนีเข้ามา
struct MyTestIndexedFixture {
bool x ;
bool y ;
};
UTEST_I_SETUP ( MyTestIndexedFixture ) {
if ( utest_index < 30 ) {
utest_fixture -> x = utest_index & 1 ;
utest_fixture -> y = ( utest_index + 1 ) & 1 ;
}
}
UTEST_I_TEARDOWN ( MyTestIndexedFixture ) {
EXPECT_LE ( 0 , utest_index );
}
UTEST_I ( MyTestIndexedFixture , a , 2 ) {
ASSERT_TRUE ( utest_fixture -> x | utest_fixture -> y );
}
UTEST_I ( MyTestIndexedFixture , b , 42 ) {
// this will fail when the index is >= 30
ASSERT_TRUE ( utest_fixture -> x | utest_fixture -> y );
}บันทึก:
การจับคู่สิ่งที่ GoogleTest มีเรามีสองสายพันธุ์ของแต่ละเงื่อนไขการตรวจสอบข้อผิดพลาด - ยืนยันและคาดหวัง หากการยืนยันล้มเหลวกรณีทดสอบจะหยุดการดำเนินการและ utest.h จะดำเนินการต่อกับกรณีทดสอบถัดไปที่จะเรียกใช้ หากความคาดหวังล้มเหลวส่วนที่เหลือของกรณีทดสอบจะยังคงดำเนินการอยู่เพื่อให้สามารถตรวจสอบเพิ่มเติมได้
ขณะนี้เราให้มาโครต่อไปนี้ที่จะใช้ภายใน UTESTS:
ยืนยันว่า x ประเมินเป็นจริง (เช่นไม่ใช่ศูนย์)
UTEST ( foo , bar ) {
int i = 1 ;
ASSERT_TRUE ( i ); // pass!
ASSERT_TRUE ( 42 ); // pass!
ASSERT_TRUE ( 0 ); // fail!
}ยืนยันว่า x ประเมินเป็นเท็จ (เช่นศูนย์)
UTEST ( foo , bar ) {
int i = 0 ;
ASSERT_FALSE ( i ); // pass!
ASSERT_FALSE ( 1 ); // fail!
}ยืนยันว่า x และ y เท่ากัน
UTEST ( foo , bar ) {
int a = 42 ;
int b = 42 ;
ASSERT_EQ ( a , b ); // pass!
ASSERT_EQ ( a , 42 ); // pass!
ASSERT_EQ ( 42 , b ); // pass!
ASSERT_EQ ( 42 , 42 ); // pass!
ASSERT_EQ ( a , b + 1 ); // fail!
}ยืนยันว่า X และ Y ไม่เท่ากัน
UTEST ( foo , bar ) {
int a = 42 ;
int b = 13 ;
ASSERT_NE ( a , b ); // pass!
ASSERT_NE ( a , 27 ); // pass!
ASSERT_NE ( 69 , b ); // pass!
ASSERT_NE ( 42 , 13 ); // pass!
ASSERT_NE ( a , 42 ); // fail!
}ยืนยันว่า x น้อยกว่า y
UTEST ( foo , bar ) {
int a = 13 ;
int b = 42 ;
ASSERT_LT ( a , b ); // pass!
ASSERT_LT ( a , 27 ); // pass!
ASSERT_LT ( 27 , b ); // pass!
ASSERT_LT ( 13 , 42 ); // pass!
ASSERT_LT ( b , a ); // fail!
}ยืนยันว่า x น้อยกว่าหรือเท่ากับ y
UTEST ( foo , bar ) {
int a = 13 ;
int b = 42 ;
ASSERT_LE ( a , b ); // pass!
ASSERT_LE ( a , 27 ); // pass!
ASSERT_LE ( a , 13 ); // pass!
ASSERT_LE ( 27 , b ); // pass!
ASSERT_LE ( 42 , b ); // pass!
ASSERT_LE ( 13 , 13 ); // pass!
ASSERT_LE ( 13 , 42 ); // pass!
ASSERT_LE ( b , a ); // fail!
}ยืนยันว่า x มากกว่า y
UTEST ( foo , bar ) {
int a = 42 ;
int b = 13 ;
ASSERT_GT ( a , b ); // pass!
ASSERT_GT ( a , 27 ); // pass!
ASSERT_GT ( 27 , b ); // pass!
ASSERT_GT ( 42 , 13 ); // pass!
ASSERT_GT ( b , a ); // fail!
}ยืนยันว่า x มากกว่าหรือเท่ากับ y
UTEST ( foo , bar ) {
int a = 42 ;
int b = 13 ;
ASSERT_GE ( a , b ); // pass!
ASSERT_GE ( a , 27 ); // pass!
ASSERT_GE ( a , 13 ); // pass!
ASSERT_GE ( 27 , b ); // pass!
ASSERT_GE ( 42 , b ); // pass!
ASSERT_GE ( 13 , 13 ); // pass!
ASSERT_GE ( 42 , 13 ); // pass!
ASSERT_GE ( b , a ); // fail!
}ยืนยันว่าสตริง x และ y เท่ากัน
UTEST ( foo , bar ) {
char * a = "foo" ;
char * b = "bar" ;
ASSERT_STREQ ( a , a ); // pass!
ASSERT_STREQ ( b , b ); // pass!
ASSERT_STREQ ( a , b ); // fail!
}ยืนยันว่าสตริง x และ y ไม่เท่ากัน
UTEST ( foo , bar ) {
char * a = "foo" ;
char * b = "bar" ;
ASSERT_STRNE ( a , b ); // pass!
ASSERT_STRNE ( a , a ); // fail!
}ยืนยันว่าสตริง x และ y เท่ากับความยาวของสตริง x
UTEST ( foo , bar ) {
char * a = "foobar" ;
char * b = "foo" ;
ASSERT_STRNEQ ( a , a ); // pass!
ASSERT_STRNEQ ( b , b ); // pass!
ASSERT_STRNEQ ( a , b ); // pass!
}ยืนยันว่าสตริง x และ y ไม่เท่ากับความยาวของสตริง x
UTEST ( foo , bar ) {
char * a = "foobar" ;
char * b = "bar" ;
ASSERT_STRNNE ( a , b ); // pass!
ASSERT_STRNNE ( a , a ); // fail!
}ยืนยันว่าค่าจุดลอยตัว x และ y อยู่ในระยะ epsilon ของกันและกัน
UTEST ( foo , bar ) {
float a = 42.0f ;
float b = 42.01f ;
ASSERT_NEAR ( a , b , 0.01f ); // pass!
ASSERT_NEAR ( a , b , 0.001f ); // fail!
}ยืนยันว่า Exception_type จะถูกโยนลงเมื่อมีการดำเนินการรหัส X
void foo ( int bar) {
if (bar == 1 )
throw std::range_error;
}
UTEST (foo, bar) {
ASSERT_EXCEPTION ( foo ( 1 ), std::range_error); // pass!
ASSERT_EXCEPTION ( foo ( 2 ), std::range_error); // fail!
ASSERT_EXCEPTION ( foo ( 1 ), std:: exception ); // fail!
}คาดว่า X จะประเมินเป็นจริง (เช่นไม่ใช่ศูนย์)
UTEST ( foo , bar ) {
int i = 1 ;
EXPECT_TRUE ( i ); // pass!
EXPECT_TRUE ( 42 ); // pass!
EXPECT_TRUE ( 0 ); // fail!
}คาดว่า X จะประเมินเป็นเท็จ (เช่นศูนย์)
UTEST ( foo , bar ) {
int i = 0 ;
EXPECT_FALSE ( i ); // pass!
EXPECT_FALSE ( 1 ); // fail!
}คาดว่า X และ Y จะเท่ากัน
UTEST ( foo , bar ) {
int a = 42 ;
int b = 42 ;
EXPECT_EQ ( a , b ); // pass!
EXPECT_EQ ( a , 42 ); // pass!
EXPECT_EQ ( 42 , b ); // pass!
EXPECT_EQ ( 42 , 42 ); // pass!
EXPECT_EQ ( a , b + 1 ); // fail!
}คาดว่า X และ Y จะไม่เท่ากัน
UTEST ( foo , bar ) {
int a = 42 ;
int b = 13 ;
EXPECT_NE ( a , b ); // pass!
EXPECT_NE ( a , 27 ); // pass!
EXPECT_NE ( 69 , b ); // pass!
EXPECT_NE ( 42 , 13 ); // pass!
EXPECT_NE ( a , 42 ); // fail!
}คาดว่า X จะน้อยกว่า Y
UTEST ( foo , bar ) {
int a = 13 ;
int b = 42 ;
EXPECT_LT ( a , b ); // pass!
EXPECT_LT ( a , 27 ); // pass!
EXPECT_LT ( 27 , b ); // pass!
EXPECT_LT ( 13 , 42 ); // pass!
EXPECT_LT ( b , a ); // fail!
}คาดว่า x น้อยกว่าหรือเท่ากับ y
UTEST ( foo , bar ) {
int a = 13 ;
int b = 42 ;
EXPECT_LE ( a , b ); // pass!
EXPECT_LE ( a , 27 ); // pass!
EXPECT_LE ( a , 13 ); // pass!
EXPECT_LE ( 27 , b ); // pass!
EXPECT_LE ( 42 , b ); // pass!
EXPECT_LE ( 13 , 13 ); // pass!
EXPECT_LE ( 13 , 42 ); // pass!
EXPECT_LE ( b , a ); // fail!
}คาดว่า x จะมากกว่า y
UTEST ( foo , bar ) {
int a = 42 ;
int b = 13 ;
EXPECT_GT ( a , b ); // pass!
EXPECT_GT ( a , 27 ); // pass!
EXPECT_GT ( 27 , b ); // pass!
EXPECT_GT ( 42 , 13 ); // pass!
EXPECT_GT ( b , a ); // fail!
}คาดว่า x จะมากกว่าหรือเท่ากับ y
UTEST ( foo , bar ) {
int a = 42 ;
int b = 13 ;
EXPECT_GE ( a , b ); // pass!
EXPECT_GE ( a , 27 ); // pass!
EXPECT_GE ( a , 13 ); // pass!
EXPECT_GE ( 27 , b ); // pass!
EXPECT_GE ( 42 , b ); // pass!
EXPECT_GE ( 13 , 13 ); // pass!
EXPECT_GE ( 42 , 13 ); // pass!
EXPECT_GE ( b , a ); // fail!
}คาดว่าสตริง x และ y เท่ากัน
UTEST ( foo , bar ) {
char * a = "foo" ;
char * b = "bar" ;
EXPECT_STREQ ( a , a ); // pass!
EXPECT_STREQ ( b , b ); // pass!
EXPECT_STREQ ( a , b ); // fail!
}คาดว่าสตริง x และ y จะไม่เท่ากัน
UTEST ( foo , bar ) {
char * a = "foo" ;
char * b = "bar" ;
EXPECT_STRNE ( a , b ); // pass!
EXPECT_STRNE ( a , a ); // fail!
}คาดว่าสตริง x และ y จะเท่ากับความยาวของสตริง x
UTEST ( foo , bar ) {
char * a = "foobar" ;
char * b = "foo" ;
EXPECT_STRNEQ ( a , a ); // pass!
EXPECT_STRNEQ ( b , b ); // pass!
EXPECT_STRNEQ ( a , b ); // pass!
}คาดว่าสตริง x และ y จะไม่เท่ากับความยาวของสตริง x
UTEST ( foo , bar ) {
char * a = "foobar" ;
char * b = "bar" ;
EXPECT_STRNNE ( a , b ); // pass!
EXPECT_STRNNE ( a , a ); // fail!
}คาดว่าค่าจุดลอยตัว x และ y อยู่ในระยะ epsilon ของกันและกัน
UTEST ( foo , bar ) {
float a = 42.0f ;
float b = 42.01f ;
EXPECT_NEAR ( a , b , 0.01f ); // pass!
EXPECT_NEAR ( a , b , 0.001f ); // fail!
}คาดว่า Exception_type จะถูกโยนลงเมื่อมีการดำเนินการรหัส X
void foo ( int bar) {
if (bar == 1 )
throw std::range_error;
}
UTEST (foo, bar) {
EXPECT_EXCEPTION ( foo ( 1 ), std::range_error); // pass!
EXPECT_EXCEPTION ( foo ( 2 ), std::range_error); // fail!
EXPECT_EXCEPTION ( foo ( 1 ), std:: exception ); // fail!
}คาดว่า Exception_type จะถูกโยนด้วย Message Exception_message เมื่อมีการดำเนินการรหัส X
void foo ( int bar) {
if (bar == 1 )
throw std::range_error ( " bad bar " );
}
UTEST (foo, bar) {
EXPECT_EXCEPTION_WITH_MESSAGE ( foo ( 1 ), std::range_error, " bad bar " ); // pass!
EXPECT_EXCEPTION_WITH_MESSAGE ( foo ( 2 ), std::range_error, " bad bar2 " ); // fail!
EXPECT_EXCEPTION_WITH_MESSAGE ( foo ( 1 ), std:: exception , " bad bar " ); // fail!
} แมโครนี้ช่วยให้คุณทำเครื่องหมายกรณีทดสอบว่าถูกข้าม - เช่น ว่ากรณีทดสอบจะไม่ถูกดำเนินการ การทดสอบจะหยุดทำงานในขณะที่คุณดำเนินการมาโครรายงาน msg เป็นเหตุผลสำหรับการข้ามและทำเครื่องหมายการทดสอบว่า 'ข้าม' สิ่งเหล่านี้จะถูกรายงานในตอนท้ายของการดำเนินการก่อนที่ความล้มเหลวและการข้ามกรณีทดสอบจะ ไม่ ทำให้กระบวนการออกไปด้วยรหัสที่ไม่เป็นศูนย์
UTEST ( foo , bar ) {
UTEST_SKIP ( "Need to implement this test!" );
}นอกจากนี้เพื่อให้มีความเป็นไปได้ที่จะมีข้อความที่กำหนดเองในการทดสอบความผิดพลาดแมโครทั้งหมดสามารถใช้กับคำต่อท้ายที่เรียกว่า "_MSG" ซึ่งได้รับพารามิเตอร์พิเศษซึ่งเป็นสตริงที่มีข้อความที่กำหนดเองเพื่อพิมพ์ในกรณีที่ล้มเหลว
ตัวอย่างเช่น:
UTEST ( foo , bar ) {
int i = 1 ;
EXPECT_TRUE_MSG ( i , "custom message" ); // pass!
EXPECT_TRUE_MSG ( 42 , "custom message" ); // pass!
EXPECT_TRUE_MSG ( 0 , "custom message" ); // fail! (with the following output)
} test.cpp:42: Failure
Expected : true
Actual : false
Message : custom message
[ FAILED ] foo.bar (8086ns)
ห้องสมุดรองรับการยืนยันประเภทจำนวนเต็มในตัว, จุดลอยตัวหรือตัวชี้
นี่คือซอฟต์แวร์ฟรีและไม่มีภาระผูกพันที่ปล่อยออกสู่โดเมนสาธารณะ
ทุกคนมีอิสระที่จะคัดลอกแก้ไขเผยแพร่ใช้รวบรวมขายหรือแจกจ่ายซอฟต์แวร์นี้ไม่ว่าจะในรูปแบบซอร์สโค้ดหรือเป็นไบนารีที่รวบรวมเพื่อวัตถุประสงค์ใด ๆ เชิงพาณิชย์หรือไม่ใช่เชิงพาณิชย์และทุกวิธี
ในเขตอำนาจศาลที่รับรู้กฎหมายลิขสิทธิ์ผู้แต่งหรือผู้แต่งซอฟต์แวร์นี้อุทิศความสนใจด้านลิขสิทธิ์ใด ๆ และทั้งหมดในซอฟต์แวร์ให้กับโดเมนสาธารณะ เราทุ่มเทนี้เพื่อประโยชน์ของสาธารณชนโดยรวมและความเสียหายของทายาทและผู้สืบทอดของเรา เราตั้งใจที่จะอุทิศตนนี้เป็นการกระทำที่เปิดเผยอย่างเปิดเผยในความเป็นอมตะของสิทธิในปัจจุบันและอนาคตทั้งหมดของซอฟต์แวร์นี้ภายใต้กฎหมายลิขสิทธิ์
ซอฟต์แวร์มีให้ "ตามสภาพ" โดยไม่มีการรับประกันใด ๆ ไม่ว่าโดยชัดแจ้งหรือโดยนัยรวมถึง แต่ไม่ จำกัด เฉพาะการรับประกันความสามารถในการค้าการออกกำลังกายสำหรับวัตถุประสงค์เฉพาะและการไม่เข้าร่วม ไม่ว่าในกรณีใดผู้เขียนจะต้องรับผิดชอบต่อการเรียกร้องความเสียหายหรือความรับผิดอื่น ๆ ไม่ว่าจะเป็นการกระทำของสัญญาการละเมิดหรืออื่น ๆ ที่เกิดขึ้นจากหรือเกี่ยวข้องกับซอฟต์แวร์หรือการใช้งานหรือการติดต่ออื่น ๆ ในซอฟต์แวร์
สำหรับข้อมูลเพิ่มเติมโปรดดูที่ http://unlicense.org/