【C++】Google Gtest测试框架的使用

本文首发于 ❄️慕雪的寒舍

gtest模块的安装参考站内教程 ubuntu安装google gtest

本文使用的gtest版本为1.14.0;

1.gtest是用来干嘛的?

google gtest是一个c++的单元测试模块,它提供了一系列规范化的宏,来帮助我们进行函数的单元测试。

image.png

单元测试你可以理解为测试我们编写好的每个函数模块,确保这些函数的功能不会影响其他函数,并保证函数能依照预期的功能进行工作。

要知道,绝大部分的软件bug都是在设计、初次代码编写的过程中产生的,比如你写了一个add函数,但是错误的将运算符写成了-,add就变成sub函数了,如果这个add函数在其他功能模块完成之后才发现被写错,就闹大笑话了。

当然,上面这个add的例子并不合适,因为它太简单了。但实际项目中,就是由多个很简单的代码聚合而成的一个大软件模块。每一个看上去简单、不可能写错的地方,都有可能隐藏的出错的危险。

所以,这就需要我们在完成每个功能函数的编写后,通过单元测试来判断函数是否有问题。

2.gtest的代码基本框架

一般情况下,多模块的软件项目都会用cmake来实现批量化的编译和单元测试的运行。但本文只是对gtest模块使用的最基本教程,再加上我并没有学习cmake的使用,所以暂时使用g++直接对单模块进行编译并介绍gtest的测试宏。

Testing Reference | GoogleTest

比较常用的是TEST和TEST_F这两个测试宏,更多测试宏请参考官方文档中的介绍。

2.1 TEST 单元测试模块

在gtest中,一个单元测试模块长下面这样

TEST(TestSuiteName, TestName) {
  ... statements ...
}

你可以理解为,左侧是被测模块的名字,右侧是在这个被测模块中的某个测试的名字,statements是任意被测模块的代码;gtest框架建议使用大驼峰的命名方式,TEST的命名中不要带有_下划线。

比如我有一个模块A的单元测试,那么左侧可以填写为模块ATest,右侧填写为类中某个函数的测试。这两个名字可以随便起,但是在同一个TestSuiteName中不能有两个相同的TestName;

一个单元测试模块的成功与失败取决于内部定义的gtest断言宏,参考后文的介绍。

2.2 TEST_F 类测试模块

上述的TEST方式是用于测试普通函数的,还有一个TEST_F可以用于对类进行测试

TEST_F(TestFixtureName, TestName) {
  ... statements ...
}

此时左侧的TestFixtureName不再是随便起的了,你必须定义一个继承于testing::Test的测试类,该类可以定义成员变量或对被测目标进行初始化、销毁操作。

class MyClassTest : public testing::Test {
protected:  
    void SetUp() // 初始化,在每个TEST_F中都会被调用
    {}

    void TearDown() // 销毁,在每个TEST_F结束时都会调用
    {}
    
	// 可以定义一些成员变量,在TEST_F中能访问
    int _a;
};

最终的测试代码应该是下面这样的

class MyClassTest : public testing::Test {
  ...
};

TEST_F(MyClassTest, HasPropertyA) { ... }
TEST_F(MyClassTest, HasPropertyB) { ... }

2.3 简单示例

下面是一个简单的gtest单元测试编写的示例,包含测试单元体和main函数。一个文件里面可以写N个TEST或TEST_F,它们会按顺序执行。

#include <gtest/gtest.h>

// 一个函数测试
// 左侧是测试模块的名字,右侧是该模块测试的目的
TEST(ADDTEST, ADDTEST_TRUE)
{}

int main(int argc, char **argv)
{
	// 主函数
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

使用如下g++命令编译该代码,注意gtest是一个动态库,需要使用-lgtest进行链接。

g++ test.cpp -o test -lgtest

3.gtest提供的断言宏

官方文档:Assertions Reference | GoogleTest

gtest中提供的宏分为ASSERT和EXPECT两种,其中ASSERT宏会在检查到错误的时候直接终止单元测试用例的运行(注意是单个单元测试),而EXPECT不会。

TEST(EXP,EXP1)
{
	ASSERT_EQ(1,1); // 如果这个出错了,后续不会执行
	// 这里不会被执行	
}

TEST(EXP,EXP2)
{
	EXPECT_EQ(1,1); // 如果这个出错了,还是会继续往后执行这个模块的其他代码。
	// 这里的代码能被执行
}

单元测试运行结束后,gtest会给出测试失败的模块汇总。

3.1 相等/大小判断

ASSERT宏EXPECT宏功能参数个数
ASSERT_TRUEEXPECT_TRUE判真1
ASSERT_FALSEEXPECT_FALSE判假1
ASSERT_EQEXPECT_EQ相等2
ASSERT_NEEXPECT_NE不相等2
ASSERT_GTEXPECT_GT第一个参数是否大于第二个参数2
ASSERT_LTEXPECT_LT小于(原理同上)2
ASSERT_GEEXPECT_GE大于等于2
ASSERT_LEEXPECT_LE小于等于2
ASSERT_FLOAT_EQEXPECT_FLOAT_EQ单精度浮点数相等2
ASSERT_DOUBLE_EQEXPECT_DOUBLE_EQ双精度浮点数相等2
ASSERT_NEAREXPECT_NEAR浮点数是否接近(第三个参数为允许的误差值)3
ASSERT_STREQEXPECT_STREQC字符串相等2
ASSERT_STRNEEXPECT_STRNEC字符串不相等2
ASSERT_STRCASEEQEXPECT_STRCASEEQC字符串相等(忽略大小写)2
ASSERT_STRCASENEEXPECT_STRCASENEC字符串不相等(忽略大小写)2
ASSERT_PRED1EXPECT_PRED1自定义谓词测试(有1到5级,对应不同参数个数的自定义函数)2
ASSERT_THATEXPECT_THAT判断函数返回值是否符合给定的matcher(gtest提供的)2

3.2 异常相关

除了上述这种判断大小和相等的宏,还有和异常以及程序崩溃相关的宏

ASSERT宏EXPECT宏作用参数个数
ASSERT_THROWEXPECT_THROW期待抛出指定异常,第一个参数是目标函数,第二个参数是异常类型2
ASSERT_ANY_THROWEXPECT_ANY_THROW期待抛出任何异常1
ASSERT_NO_THROWEXPECT_NO_THROW不希望抛出任何异常1
ASSERT_EXITEXPECT_EXIT期望程序以指定错误码exit,且标准错误输出符合第三个参数的regex表达式3
ASSERT_DEATHEXPECT_DEATH期望程序错误退出(退出码非0),且标准错误输出符合第二个参数的regex表达式2
ASSERT_DEBUG_DEATHEXPECT_DEBUG_DEATH同上,但是在调试模式下测试;非调试模式下只会执行函数,不做判断。2
ASSERT_DEATH_IF_SUPPORTEDEXPECT_DEATH_IF_SUPPORTED同DEATH,但是只有在支持的时候才会被调用,如果不支持则什么都不做。2

3.3 直接表明成功和失败

还有两个宏是直接表明该单元测试是否成功,以及是否失败的。主动调用这两个宏会提前终止该单元测试用例。

SUCCEED(); // 成功
FAIL();    // 失败

3.4 添加失败信息

有的时候gtest默认提供的ASSERT宏不够我们的使用,你可以用自定义的判断,并在不符合预期的时候将这个错误信息添加进去,Gtest在最后汇总的时候也会显示出来。

// Generates a nonfatal failure, which allows the current function to continue running.
ADD_FAILURE();

// Generates a nonfatal failure at the file and line number specified.
ADD_FAILURE_AT(file_path,line_number);

这两个宏的效果和EXPECT类似,都允许继续往后执行该单元测试用例。

4.使用示例

大部分的使用都是一样的,下面只对几个有代表性的做使用示例;为了方便,只对ASSERT做示例,因为它的效果和EXPECT完全一致,上文已经提到了二者的区别了。

4.1 ASSERT_EXIT

ASSERT_EXIT有三个参数,分别为待测函数、退出码或退出信号、错误信息regex;待测函数必须以指定的错误码或错误信号退出程序,并在stderr中打印能被这个regex匹配的错误信息。

ASSERT_EXIT(statement,predicate,matcher);

第二个参数的可选项,分别代表错误退出码和收到的错误信号

// Returns true if the program exited normally with the given exit status code.
::testing::ExitedWithCode(exit_code);

// Returns true if the program was killed by the given signal.
// Not available on Windows.
::testing::KilledBySignal(signal_number);

第三个参数在官网上的描述是这样的

The parameter matcher is either a matcher for a const std::string&, or a regular expression (see Regular Expression Syntax)—a bare string s (with no matcher) is treated as ContainsRegex(s), not Eq(s).

如果传入一个普通字符串,则会判断stderr输出的内容是否包含该字符串。

下面是一个简单的示例,我们的函数调用了exit(1),使用ASSERT_EXIT来判断它是否以预期的错误码1退出程序。第三个参数.*是正则表达式,代表任意错误信息的匹配。

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>


void test_exit()
{
    std::cerr << "test exit\n";
    exit(1);
    // int ret = 10/0;
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    // 期望以错误码1退出
    ASSERT_EXIT(test_exit(),testing::ExitedWithCode(1),".*");
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

编译后的执行效果如下,我们的单元测试成功了,因为函数的确是以错误码1退出的。

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
[       OK ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

那么如果换一个退出方式呢?比如因为除0错误退出

❯ g++ test.cpp -o test -lgtest && ./test
test.cpp: In function 'void test_exit()':
test.cpp:11:17: warning: division by zero [-Wdiv-by-zero]
   11 |     int ret = 10/0;
      |               ~~^~
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
test.cpp:17: Failure
Death test: test_exit()
    Result: died but not with expected exit code:
            Terminated by signal 8
Actual msg:
[  DEATH   ] test exit
[  DEATH   ] 

[  FAILED  ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_EXIT

 1 FAILED TEST

这个时候单元测试就失败了,并会打印出失败行的位置和失败的原因。这里提到失败是因为受到了信号8,但我们预期是错误码1。

将预期修改为信号8,就能通过测试

void test_exit()
{
    std::cerr << "test exit\n";
    // exit(1);
    int ret = 10/0;
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    // 期望以收到信号8退出
    ASSERT_EXIT(test_exit(),testing::KilledBySignal(8),".*");
}

注意这里的警告是g++编译器检测到除0错误后提供的,并非是运行时的错误。

❯ g++ test.cpp -o test -lgtest && ./test
test.cpp: In function 'void test_exit()':
test.cpp:11:17: warning: division by zero [-Wdiv-by-zero]
   11 |     int ret = 10/0;
      |               ~~^~
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
[       OK ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

将第三个参数改为一个普通字符串,gtest会进行stderr输出是否包含该字符串的检查;

void test_exit()
{
    std::cerr << "test exit\n";
    int ret = 10/0;
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    // 期望以收到信号退出
    ASSERT_EXIT(test_exit(),testing::KilledBySignal(8),"happy");
}

可以看到,单元测试失败的原因是“错误退出但是没有提供期望的error输出”。

❯ g++ test.cpp -o test -lgtest && ./test
test.cpp: In function 'void test_exit()':
test.cpp:10:17: warning: division by zero [-Wdiv-by-zero]
   10 |     int ret = 10/0;
      |               ~~^~
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
test.cpp:16: Failure
Death test: test_exit()
    Result: died but not with expected error.
  Expected: contains regular expression "happy"
Actual msg:
[  DEATH   ] test exit
[  DEATH   ] 

[  FAILED  ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_EXIT

 1 FAILED TEST

如果在错误输出中包含happy字符串,则正常通过测试。

void test_exit()
{
    std::cerr << "test exit happy\n"; // 包含happy字符串
    int ret = 10/0;
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    // 期望以收到信号退出
    ASSERT_EXIT(test_exit(),testing::KilledBySignal(8),"happy");
}
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
[       OK ] EXPTEST.EXPTEST_EXIT (1 ms)
[----------] 1 test from EXPTEST (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 1 test.

注意:函数内的assert错误对应的是信号6,如果需要用ASSERT_EXIT来捕捉assert错误,则需要使用testing::KilledBySignal(6)

4.2 ASSERT_DEATH

ASSERT_DEATH和ASSERT_EXIT宏的作用基本一致,只不过ASSERT_DEATH不需要我们传入期望退出的错误码或信号。此时任意非0错误码退出和任意信号退出都会被视为ASSERT_DEATH的测试成功情况。

EXPECT_DEATH(statement,matcher);
ASSERT_DEATH(statement,matcher);

这里的第二个参数和ASSERT_EXIT的第三个参数一致,可以是一个字符串,也可以是一个regex表达式。

示例代码和测试结果如下:

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>


void test_exit()
{
    std::cerr << "test exit\n";
    exit(1);
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    ASSERT_DEATH(test_exit(),".*");
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
[       OK ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

ASSERT_DEATH只有在被测函数没有错误退出,或者以exit(0)退出的时候会出错。因为0号在操作系统中是进程退出的正常情况,非0才是错误信号。

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
test.cpp:15: Failure
Death test: test_exit()
    Result: died but not with expected exit code:
            Exited with exit status 0
Actual msg:
[  DEATH   ] test exit
[  DEATH   ] 

[  FAILED  ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_EXIT

 1 FAILED TEST

注意,抛出异常并没有归结到DEATH和EXIT的判定范围内。

void test_exit()
{
    std::cerr << "test exit happy\n";
    // int ret = 10/0;
    throw std::runtime_error("123");
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    ASSERT_DEATH(test_exit(),"happy");
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
test.cpp:16: Failure
Death test: test_exit()
    Result: threw an exception.
 Error msg:
[  DEATH   ] test exit happy
[  DEATH   ] 
[  DEATH   ] test.cpp:16:: Caught std::exception-derived exception escaping the death test statement. Exception message: 123
[  DEATH   ] 

[  FAILED  ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_EXIT

 1 FAILED TEST

4.3 ASSERT_DEBUG_DEATH

对于ASSERT_DEBUG_DEATH,官方文档是这么描述的:

In debug mode, behaves the same as EXPECT_DEATH. When not in debug mode (i.e. NDEBUG is defined), just executes statement.

这里补充一下NDEBUG宏的作用,在标准库里面,它会控制assert是否起效果。如果一个程序define了NDEBUG宏(注意必须在引用<assert.h>头文件之前定义),那么assert将什么都不做。

可以看assert.h的源代码,当定义了NDEBUG宏后,assert会调用__ASSERT_VOID_CAST (0);在C++中这个cast是一个static_cast<void>,在C语言中是一个)void)的强转,反正都是啥都不干。

#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif

/* void assert (int expression);

   If NDEBUG is defined, do nothing.
   If not, and EXPRESSION is zero, print an error message and abort.  */

#ifdef	NDEBUG

# define assert(expr)		(__ASSERT_VOID_CAST (0))

/* void assert_perror (int errnum);

   If NDEBUG is defined, do nothing.  If not, and ERRNUM is not zero, print an
   error message with the error text for ERRNUM and abort.
   (This is a GNU extension.) */

# ifdef	__USE_GNU
#  define assert_perror(errnum)	(__ASSERT_VOID_CAST (0))
# endif

#else /* Not NDEBUG.  */

下面是一个简单的测试示例,当我们没有定义NDEBUG的时候,ASSERT_DEBUG_DEATH和ASSERT_DEATH做的是相同的操作。

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>


void test_exit()
{
    std::cerr << "test exit happy\n";
    exit(1);
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    ASSERT_DEBUG_DEATH(test_exit(),"happy");
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
[       OK ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

如果在文件开头定义了NDEBUG,那么ASSERT_DEBUG_DEATH则只会调用函数,并不会做错误信息的判断,如下所示,我们将函数中的exit删除,gtest也没有报告错误。

#define NDEBUG 1 // 一定要在开头定义
#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>


void test_exit()
{
    std::cerr << "test exit happy\n";
    // exit(1); // 没有错误退出
}

TEST(EXPTEST, EXPTEST_EXIT)
{
    ASSERT_DEBUG_DEATH(test_exit(),"happy");
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

可以看到,我们的函数并没有退出,但是由于定义了NDEBUG,ASSERT_DEBUG_DEATH宏没有报告错误。(去掉该宏,则会和ASSERT_DEATH一样提示出错)

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EXIT
test exit happy
[       OK ] EXPTEST.EXPTEST_EXIT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

4.4 ASSERT_THROW

调用对象应该抛出异常,并判断异常类型是否为期待类型。

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>

void test_throw()
{
    throw std::runtime_error("test");
}

TEST(EXPTEST, EXPTEST_THROW)
{
    ASSERT_THROW(test_throw(),std::runtime_error);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_THROW
[       OK ] EXPTEST.EXPTEST_THROW (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

当抛出的异常类型不一致的时候会出错

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_THROW
test.cpp:12: Failure
Expected: test_throw() throws an exception of type std::runtime_error.
  Actual: it throws std::length_error with description "123".

[  FAILED  ] EXPTEST.EXPTEST_THROW (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_THROW

 1 FAILED TEST

异常相关的还有两个

  • ASSERT_ANY_THROW不需要传入第二个参数,只关注目标函数应该抛出异常;
  • ASSERT_NO_THROW也只有一个参数,目标函数不应该抛出异常;

因为它们很简单,这里就不做演示了。

4.5 ASSERT_EQ/NE

相等和不相等的比较。

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>


TEST(EXPTEST, EXPTEST_EQ)
{
    int ret = 10;
    ASSERT_EQ(ret,10);
    ASSERT_NE(ret,29);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EQ
[       OK ] EXPTEST.EXPTEST_EQ (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

ASSERT_FLOAT_EQ和ASSERT_DOUBLE_EQ是在比较的基础上允许一定的浮点数精度误差值。如果你需要比较两个浮点数,请使用对应的浮点数类型比较宏,而不要直接使用ASSERT_EQ;

4.6 ASSERT_GE/LE

注意,大小写比较都是左和右直接按顺序比较的,比如GE是判断左边是否大于右边(不要搞反顺序了)

TEST(EXPTEST, EXPTEST_GE)
{
    int ret = 10;
    ASSERT_GE(ret,5);
    ASSERT_LE(ret,29);
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_EQ
[       OK ] EXPTEST.EXPTEST_EQ (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

4.7 ASSERT_NEAR

该宏提供了第三个参数,在浮点数比较时允许一定的误差值。官网文档说明是保证val1和val2不超过abs_error的误差边界。

EXPECT_NEAR(val1,val2,abs_error)
ASSERT_NEAR(val1,val2,abs_error)
// Verifies that the difference between val1 and val2 does not exceed the absolute error bound abs_error.

下面是一个示例

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>


TEST(EXPTEST, EXPTEST_NEAR)
{
    ASSERT_NEAR(3.14,3.15,0.01);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

因为两个数字的差距的确在0.01的误差区间,所以可以通过测试。

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_NEAR
[       OK ] EXPTEST.EXPTEST_NEAR (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

如果将第三个参数的误差改为0.001,则无法通过测试。

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_NEAR
test.cpp:8: Failure
The difference between 3.14 and 3.15 is 0.0099999999999997868, which exceeds 0.001, where
3.14 evaluates to 3.1400000000000001,
3.15 evaluates to 3.1499999999999999, and
0.001 evaluates to 0.001.

[  FAILED  ] EXPTEST.EXPTEST_NEAR (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_NEAR

 1 FAILED TEST

4.8 ASSERT_STREQ/STRCASEEQ

字符串相等比较,一个不忽略大小写,一个忽略大小写;

注意,这两个宏的入参都是const char*,不能直接传入std::string进行比较。

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>
#include <string>

TEST(EXPTEST, EXPTEST_STR)
{
    std::string str1 = "hello";
    std::string str2 = "hello";
    std::string str3 = "Hello";
    ASSERT_STREQ(str1.c_str(),str2.c_str());
    ASSERT_STRCASEEQ(str1.c_str(),str3.c_str());
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_STR
[       OK ] EXPTEST.EXPTEST_STR (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

4.9 ASSERT_TRUE/FALSE

bool值真假的判断。

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>
#include <string>

TEST(EXPTEST, EXPTEST_TRUE)
{
    int a, b;
    a = 1;
    b = 2;
    ASSERT_TRUE(a != b);
    ASSERT_FALSE(a == b);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_TRUE
[       OK ] EXPTEST.EXPTEST_TRUE (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

注意,这两个宏只接受bool值或者int类型,诸如nullptr这类值都是不能被直接接受的(除非你强转)。

  • 如果你需要判断一个函数的结果是否为空指针nullptr,请使用ASSERT_EQ来判断;
  • 如果你的函数返回值用了int且用0来标识错误的情况,也建议用ASSERT_EQ来更可读的判断,而不要用ASSERT_TRUE/FALSE;
❯ g++ test.cpp -o test -lgtest && ./test
In file included from /usr/local/include/gtest/gtest-printers.h:122,
                 from /usr/local/include/gtest/gtest-matchers.h:49,
                 from /usr/local/include/gtest/internal/gtest-death-test-internal.h:47,
                 from /usr/local/include/gtest/gtest-death-test.h:43,
                 from /usr/local/include/gtest/gtest.h:65,
                 from test.cpp:1:
test.cpp: In member function 'virtual void EXPTEST_EXPTEST_TRUE_Test::TestBody()':
test.cpp:8:5: error: converting to 'bool' from 'std::nullptr_t' requires direct-initialization [-fpermissive]
    8 |     ASSERT_FALSE(nullptr);
      |     ^~~~~~~~~~~~

这里也能看出C++中nullptr和C语言中NULL的不同,使用ASSERT_FALSE来判断NULL是能通过编译的,因为它本质只是一个define的数字0,和bool值的本质是一样的。

ASSERT_FALSE(NULL);

4.10 ADD_FAILURE

添加失败信息的效果示例

#include <gtest/gtest.h>
#include <iostream>
#include <unistd.h>
#include <string>

TEST(EXPTEST, EXPTEST_TRUE)
{
    int a, b;
    a = 1;
    b = 2;
    if (a!=b)
    {
        ADD_FAILURE();
    }
    // 假设我知道被测函数的目标文件和行号
    ADD_FAILURE_AT("add.hpp",20); 
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

如果你知道出错的被测函数的文件和所在行数,使用ADD_FAILURE_AT能更好的帮助定位问题。因为ADD_FAILURE只会显示出错单元测试的文件和位置。

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_TRUE
test.cpp:13: Failure
Failed

add.hpp:20: Failure
Failed

[  FAILED  ] EXPTEST.EXPTEST_TRUE (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_TRUE

 1 FAILED TEST

这里也能看到,ADD_FAILURE后,单元测试会像EXPECT宏一样继续往后运行。

4.11 ASSERT_THAT

和ASSERT_THAT相关的matcher选项在gtest中提供了,注意需要引用gmock头文件

#include <gmock/gmock.h>

using ::testing::AllOf;
using ::testing::Gt;
using ::testing::Lt;
using ::testing::MatchesRegex;
using ::testing::StartsWith;

更多matcher可以查看官方文档:Matchers

这里还有个小细节,你会发现gtest官网文档中的所有testing命名空间前面都带了一个::,这样不管你在什么自定义的命名空间里面编写gtest的代码,都可以通过::先回到全局命名空间,再访问testing命名空间。即不会和用户自定义空间中的冲突。

另外,如果你需要使用gtest框架,应该避免自己的命名空间和gtest的命名空间重名。

示例代码如下,我们的字符串的确是以he开头的,能通过检查。

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <iostream>
#include <unistd.h>
#include <string>

TEST(EXPTEST, EXPTEST_THAT)
{
    std::string str = "hello";
    ASSERT_THAT(str.c_str(),testing::StartsWith("he"));
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_THAT
[       OK ] EXPTEST.EXPTEST_THAT (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

4.12 ASSERT_PRED

谓词测试宏一共有5个,分别接受一个自定义函数和该函数对应的参数,并判断返回值是否为bool值的true。数字就代表函数的参数个数,最多支持5个参数的自定义函数。

ASSERT_PRED1(pred,val1)
ASSERT_PRED2(pred,val1,val2)
ASSERT_PRED3(pred,val1,val2,val3)
ASSERT_PRED4(pred,val1,val2,val3,val4)
ASSERT_PRED5(pred,val1,val2,val3,val4,val5)

我们定义一个测试函数,再把两个参数传入该函数,就能得到一个结果

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <iostream>
#include <unistd.h>
#include <string>

bool is_eq(int a, int b)
{
    return a == b;
}

TEST(EXPTEST, EXPTEST_PRED)
{
    ASSERT_PRED2(is_eq, 1, 2);
    // 等价于 ASSERT_TRUE(is_eq(1,2));
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from EXPTEST
[ RUN      ] EXPTEST.EXPTEST_PRED
test.cpp:14: Failure
is_eq(1, 2) evaluates to false, where
1 evaluates to 1
2 evaluates to 2

[  FAILED  ] EXPTEST.EXPTEST_PRED (0 ms)
[----------] 1 test from EXPTEST (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] EXPTEST.EXPTEST_PRED

 1 FAILED TEST

5.TEST_F

介绍完毕断言宏了,TEST的作用想必大家也明白了,下面对TEST_F给出一个简单的示例。

在上文介绍TEST_F中提到过编写的基本框架应该如下所示

class MyClassTest : public testing::Test {
  ...
};

TEST_F(MyClassTest, HasPropertyA) { ... }
TEST_F(MyClassTest, HasPropertyB) { ... }

假设我们需要对MyClass类进行测试,那么MyClassTest类就是对MyClass进行初始化和销毁操作的,每一个TEST_F都是对MyClass中的某个成员变量进行测试。

下面是一个简单的对类的单元测试的示例,目的是确认AddX和AddY的情况符合我们的预期,能正常给成员变量添加值。

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <iostream>
#include <unistd.h>
#include <memory>
#include <string>

class MyClass
{
public:
    MyClass(int x = 0, int y = 0) : _x(x), _y(y) {}

    void AddX(int val)
    {
        _x += val;
    }

    void AddY(int val)
    {
        _y += val;
    }

    int GetX() { return _x; }
    int GetY() { return _y; }

private:
    int _x;
    int _y;
};

class MyClassTest : public testing::Test
{
protected:
    void SetUp()
    {
        std::cout << "set up for MyClass\n";
        _mc = std::make_shared<MyClass>();
    }

    void TearDown()
    {
        std::cout << "tear down for MyClass\n";
        _mc.reset();
    }

    std::shared_ptr<MyClass> _mc;
};

TEST_F(MyClassTest, MyClassTestAddX)
{
    _mc->AddX(10);
    EXPECT_EQ(_mc->GetX(), 10);
}

TEST_F(MyClassTest, MyClassTestAddY)
{
    _mc->AddY(20);
    EXPECT_EQ(_mc->GetY(), 20);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

在代码中,TEST_F体内可以访问MyClassTest的成员变量_mc,并调用它的成员函数。这样就避免了我们在每个单元测试中对一些公用的类进行多次初始化程序编写的操作。

编译运行这个代码,能看到SetUp和TearDown在每次TEST_F之前和之后都会被执行,并非只执行一次!

❯ g++ test.cpp -o test -lgtest && ./test
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from MyClassTest
[ RUN      ] MyClassTest.MyClassTestAddX
set up for MyClass
tear down for MyClass
[       OK ] MyClassTest.MyClassTestAddX (0 ms)
[ RUN      ] MyClassTest.MyClassTestAddY
set up for MyClass
tear down for MyClass
[       OK ] MyClassTest.MyClassTestAddY (0 ms)
[----------] 2 tests from MyClassTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

如果你需要测试一个类的私有成员,可以使用g++的编译命令-fno-access-control直接取消访问限定符

 g++ test.cpp -o test -fno-access-control -lgtest

此时在类外对私有成员和私有函数的直接访问不会出现编译报错。但需要注意的是,你只可以在单元测试中使用该编译命令,在生产环境中不应该这么做!

The end

对于Gtest的宏的介绍到这里就结束了。

google gtest框架中还包含了一个gmock模块,这个模块的主要作用是继承一个父类,重写虚函数,并可以在测试夹具中用EXPECT_CALL自定义重写后函数的返回值和调用次数。在我遇到的实际项目中,这个gmock模块用到的次数很少,因为它的适用范围实在是太窄了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/507066.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux之 线程池 | 单例模式的线程安全问题 | 其他锁

目录 一、线程池 1、线程池 2、线程池代码 3、线程池的应用场景 二、单例模式的线程安全问题 1、线程池的单例模式 2、线程安全问题 三、其他锁 一、线程池 1、线程池 线程池是一种线程使用模式。线程池里面可以维护一些线程。 为什么要有线程池&#xff1f; 因为在…

一文教会女朋友学会日常Git使用!Git知识总结

文章目录 一文教会女朋友学会日常Git使用&#xff01;Git知识总结一、git基本知识了解1.git简介2.git区域了解3.git常用命令 二、常用工作场景1.克隆远程仓库&#xff0c;把仓库代码拉到本地2.推送代码到远程仓库&#xff08;1&#xff09;本地代码和远程仓库版本相同&#xff…

细谈SolidWorks教育版的一些基础知识

SolidWorks教育版是一款广泛应用于工程设计和教育领域的三维建模软件。它具备直观易用的操作界面和强大的设计功能&#xff0c;为学生提供了一个学习和实践的平台。在本文中&#xff0c;我们将详细探讨SolidWorks教育版的一些基础知识&#xff0c;帮助初学者更好地了解和掌握这…

鸿蒙实战开发-如何使用三方库

使用三方库 在使用三方库之前&#xff0c;需要安装 ohpm&#xff0c;并在环境变量中配置。 在项目目录的Terminal窗口执行ohpm命令下载依赖 ohpm install yunkss/eftool 命令运行成功后&#xff0c;在项目的oh-package.json5文件中会自动添加上依赖&#xff0c;如下所示&am…

【氮化镓】GaN器件中关态应力诱导的损伤定位

概括总结&#xff1a; 这项研究通过低频1/f噪声测量方法&#xff0c;探究了在关态&#xff08;OFF-state&#xff09;应力作用下&#xff0c;AlGaN/GaN高电子迁移率晶体管&#xff08;HEMTs&#xff09;中由应力引起的损伤的定位。研究中结合了电致发光&#xff08;EL&#xf…

【Java面试题系列】基础篇

目录 基本常识标识符的命名规则八种基本数据类型的大小&#xff0c;以及他们的封装类3*0.10.3返回值是什么short s1 1; s1 s1 1;有什么错? short s1 1; s1 1;有什么错?简述&&与&的区别&#xff1f;简述break与continue、return的区别&#xff1f;Arrays类的…

(负载点电源)PCD3203高转换率同步降压40V/3A内置高低侧MOSFET只需极少外围元件

1. 产品特性 ➢ 输入电压范围&#xff1a; 4.5V~40V ➢ 最大负载&#xff1a; 3A ➢ 上下管导通电阻&#xff1a; 110mΩ/70mΩ ➢ 软启保护时间 tss&#xff1a; 1ms ➢ 工作频率范围&#xff1a; 500kHz~2.5MHz ➢ 逐周期峰值电流限制 ➢ 内部补偿 ➢ 可调的输入欠压…

这个AI 应用万人使用:真人视频转动漫、手绘风,丝滑感前所未有

视频的次元壁就这么被打破了。 在 AI 的加持下&#xff0c;真人视频变身二次元就这么简单 只需要导入原始视频&#xff0c;它就可以帮你把视频改成你想要的风格&#xff0c;比如动漫风、手绘风或者 3D 卡通风格。 这一应用一经推出立刻引起了很多人的关注 因其操作简单&#x…

蓝桥杯-穿越雷区

题目要求 需求&#xff1a;从一个方格中A点&#xff0c;按要求移动到B点。 要求&#xff1a;每次只能走上下左右&#xff0c;每次只能走一次&#xff0c;每次是轮换穿越’‘,’-两个&#xff0c;否则就会能量失衡&#xff0c;发生爆炸。 使用的算法&#xff1a;这题典型的就是使…

nginx的安装教程

文章目录 简介nginx安装windows下安装linux下安装 简介 nginx是一个开源的web服务器和反向代理服务器&#xff0c;可以用作负载均衡和HTTP缓存。它处理并发能力是十分强大的&#xff0c;能够经受高负载的考验。 正向代理 Nginx不仅可以做反向代理&#xff0c;实现负载均衡&am…

AI工作站设计方案:903-多路PCIe3.0的单CPU 学习型AI工作站

多路PCIe3.0的单CPU 学习型AI工作站 一、机箱功能和技术指标&#xff1a; 系统 系统型号 ORI-SR500 主板支持 EEB(12*13)/CEB(12*10.5)/ATX(12*9.6)/Mi cro ATX 前置硬盘 最大支持2个3.5寸1个2.5寸SATA 硬盘2个2.5寸SATA 硬盘 &#xff08;背部&#xff09; 电源类型 C…

【Django开发】前后端分离美多商城项目第4篇:用户部分,1. 判断用户名是否存在【附代码文档】

美多商城项目4.0文档完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;美多商城&#xff0c;项目准备1.B2B--企业对企业,2.C2C--个人对个人,3.B2C--企业对个人,4.C2B--个人对企业,5.O2O--线上到线下,6.F2C--工厂到个人。项目准备&#xff0c;配置1. 修改set…

阿里云服务器ECS经济型e实例怎么样?

阿里云服务器ECS经济型e系列是阿里云面向个人开发者、学生、小微企业&#xff0c;在中小型网站建设、开发测试、轻量级应用等场景推出的全新入门级云服务器&#xff0c;CPU处理器采用Intel Xeon Platinum架构处理器&#xff0c;支持1:1、1:2、1:4多种处理器内存配比&#xff0c…

vue3组合式函数

vue3的组合式函数的作用是封装和复用响应式状态的函数。只能在setup 标签的script标签汇总或者setup函数中使用。 普通的函数只能调用一次&#xff0c;但是组合式函数接受到响应式参数&#xff0c;当该值发生变化时&#xff0c;也会触发相关函数的重新加载。 如下 定义了一个…

算法(滑动窗口四)

1.串联所有单词的子串 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff…

《QDebug 2024年3月》

一、Qt Widgets 问题交流 1. 二、Qt Quick 问题交流 1.Qt5 ApplicationWindow 不能使用父组件 Window 的 transientParent 属性 ApplicationWindow 使用 transientParent 报错&#xff1a; "ApplicationWindow.transientParent" is not available due to compone…

Android车载设备开发相关名词介绍

一、通讯相关 1、ECALL 如遭遇紧急情况&#xff0c;用户可按下该键以最高优先级接通呼叫中心&#xff0c;人工坐席将同时获取客户车辆的重要数据并协助驾驶员脱离危险。 实现原理 E-Call 的核心思想是利用车载卫星定位系统获取车辆的位置信息&#xff0c;在事故发生后&#x…

性能测试入门 —— 什么是性能测试PTS?

性能测试PTS&#xff08;Performance Testing Service&#xff09;是一款简单易用&#xff0c;具备强大的分布式压测能力的SaaS压测平台。 PTS可以模拟复杂的业务场景&#xff0c;并快速精准地调度不同规模的流量&#xff0c;同时提供压测过程中多维度的监控指标和日志记录。您…

输出100~200之间的素数(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//实现素数判断函数&#xff1b; int Prime(int number) {//初始化变量值&#xff1b;int divided 2;int JudgementCondition 0;//循环判断素数&#xff1b;wh…

Git的使用【入门级】--下载github项目

1.下载Git Git for Windows Windows版本进入如上网址下载&#xff1a; 点击Download即可&#xff0c;建议下载到内存多的盘&#xff0c;我是下载到移动固态优盘中&#xff0c;以免C盘太挤。 验证Git是否安装成功&#xff1a; 双击git-bash.exe&#xff0c;命令行输入git versi…