单元测试及gtest学习
1.介绍
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
- 为每个单元编写多个测试用例,覆盖不同的输入和输出情况和边界条件(预期什么输入、预期什么输出)。
- 如果需要测试的函数依赖于外部资源(如数据库、网络调用等),可以使用 Mock 或 Stub 对其进行模拟,以隔离测试并确保独立性。
- 每个测试用例应该是独立的,不依赖于其他测试的结果。确保测试的可重复性,即每次运行测试都应该得到相同的结果。
https://zhuanlan.zhihu.com/p/387540827
2.gtest
gtest提供的sample:https://google.github.io/googletest/samples.html
用法讲解:https://blog.csdn.net/wangmj_hdu/article/details/118369609
测试中用到的断言与比较函数:https://github.com/AngryHacker/articles/blob/master/src/open_source_components/google_gtest.md
- 写单测一般包括3个部分,即Given(Mock外部依赖&准备Fake数据),When(调用被测方法)以及Then(断言执行结果)。
3.基础语法学习
//cpp文件中有一个计算阶乘的函数 //sample1.cc int Factorial(int n) { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } //对应的单元测试文件 //sample1_unittest.cc #include "sample1.h" #include "gtest/gtest.h" TEST(FactorialTest, Negative) {//宏定义,有2个参数 //第一个参数表示测试case名字,第二个是测试名字。不同的测试被分类到测试组中 // This test is named "Negative", and belongs to the "FactorialTest" // test case. EXPECT_EQ(10, Factorial(-5));//也是gtest提供的宏定义 EXPECT_EQ(1, Factorial(-1)); EXPECT_GT(Factorial(-10), 0); } // Tests factorial of 0. TEST(FactorialTest, Zero) { EXPECT_EQ(1, Factorial(0)); } int main(int argc, char **argv) { printf("Running main() from %s\n", __FILE__); //解析命令行中的GoogleTest参数,它允许用户通过多样的命令行参数来控制测试程序的行为(即定制命令行参数的行为) testing::InitGoogleTest(&argc, argv); //将会搜索不同的Test Case和不同的源文件中所有已经存在测试案例,然后运行它们,所有Test都成功时返回1,否则返回0。 return RUN_ALL_TESTS(); }
GoogleTest根据Test Case(第一个参数)对测试结果进行分组,所以一些相关的Test(第二个参数)应该放在同一个Test Case中。
4.单元测试类
namespace { // To use a test fixture, derive a class from testing::Test. class QueueTestSmpl3 : public testing::Test {//继承Test类 protected: // You should make the members protected s.t. they can be // accessed from sub-classes. static void SetUpTestSuite() {//在所有的测试用例开始之前会调用一次 std::cout<<"run before first case..."<<std::endl; } static void TearDownTestSuite() {//在所有测试用例结束之后调用一次 std::cout<<"run after last case..."<<std::endl; } // virtual void SetUp() will be called before each test is run. You // should define it if you need to initialize the variables. // Otherwise, this can be skipped. virtual void SetUp() override {//每个测试用例执行之前调用一次 std::cout<<"enter into SetUp()" <<std::endl; q1_.Enqueue(1); q2_.Enqueue(2); q2_.Enqueue(3); } // virtual void TearDown() will be called after each test is run. // You should define it if there is cleanup work to do. Otherwise, // you don't have to provide it. // virtual void TearDown() override { std::cout<<"exit from TearDown" <<std::endl; } // A helper function that some test uses. static int Double(int n) { return 2*n; } // A helper function for testing Queue::Map(). void MapTester(const Queue<int> * q) { // Creates a new queue, where each element is twice as big as the // corresponding one in q. const Queue<int> * const new_q = q->Map(Double); // Verifies that the new queue has the same size as q. ASSERT_EQ(q->Size(), new_q->Size()); // Verifies the relationship between the elements of the two queues. for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head(); n1 != nullptr; n1 = n1->next(), n2 = n2->next()) { EXPECT_EQ(2 * n1->element(), n2->element()); } delete new_q; } // Declares the variables your tests want to use. Queue<int> q0_; Queue<int> q1_; Queue<int> q2_; }; // When you have a test fixture, you define a test using TEST_F // instead of TEST. // Tests the default c'tor. TEST_F(QueueTestSmpl3, DefaultConstructor) { // You can access data in the test fixture here. EXPECT_EQ(0u, q0_.Size()); } // Tests Dequeue(). TEST_F(QueueTestSmpl3, Dequeue) { int * n = q0_.Dequeue(); EXPECT_TRUE(n == nullptr); n = q1_.Dequeue(); ASSERT_TRUE(n != nullptr); EXPECT_EQ(1, *n); EXPECT_EQ(0u, q1_.Size()); delete n; n = q2_.Dequeue(); ASSERT_TRUE(n != nullptr); EXPECT_EQ(2, *n); EXPECT_EQ(1u, q2_.Size()); delete n; } // Tests the Queue::Map() function. TEST_F(QueueTestSmpl3, Map) { MapTester(&q0_); MapTester(&q1_); MapTester(&q2_); } } // namespace