0


UT编写记录

UT编写规则 https://zhuanlan.zhihu.com/p/424117483

1、遵循AIR原则

A(automation,自动化):单元测试应该是全自动执行的,并且非交互。

I (Independence,独立性):单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。

--------反例:method2需要依赖method1的执行,将执行结果作为method2的输入。

R(Repeatable,可重复):单元测试是可以重复执行的,不能受到外界环境的影响。

2、结构

a、 单元测试代码建议有一个新的文件目录

说明:源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。

b、测试用例的包以及结构,需要保持和被测的包以及结构一致。

3、命名

a、测试类命名规范:被测试的业务+Test、被测试的接口+Test、被测试的类+Test

b、测试用例命名规范:测试用例命名应做到简洁全面,可描述。避免使用test1、test2没有含义的名称。其次需要有必要的函数方法注释。

4、禁止使用

a、不要在单元测试中使用Thread.sleep

UT测试类的封装

gtest 中Setup TearDown SetUpTestCase和TearDownTestCase 的区别_惹不起的程咬金的博客-CSDN博客_gtest teardown

使用TEST_F前需要创建一个固件类,继承于::testing::Test类。在类内部使用public或者protected描述其成员,为了保证实际执行的测试子类可以使用其成员变量。在构造函数或者继承于::testing::Test类中的SetUp方法中,可以实现我们需要构造的数据。在析构函数或者继承于::testing::Test类中的TearDown方法中,可以实现一些资源释放的代码。“构造函数/析构函数”和“SetUp/TearDown”的选择,对于什么时候选择哪对,没有统一的标准。一般来说就是构造/析构函数里忌讳做什么就不要在里面做,比如抛出异常等

全局:

class Test:public testing::Environment
{
public:
   virtual void SetUp() ;
    virtual void TearDown()  ;
};
void  Test::SetUp()
{
}
void Test::TearDown()
{
}
int main(int argc, char* argv[])
{
    testing::AddGlobalTestEnvironment(new Test);
    testing::GTEST_FLAG(output) = "xml:test.xml";
    testing::InitGoogleTest(&argc, argv);
    RUN_ALL_TESTS();
    return 0;
}

测试套件:

头文件:

class Test:public::testing::Test
{
public:
    static void SetUpTestCase() ;
    static void TearDownTestCase() ;
};

**打桩方式 **

打桩的目的主要有:隔离、补齐、控制。隔离是指将测试任务从产品项目中分离出来,使之能够独立编译、链接,并独立运行。隔离的基本方法就是打桩,将测试任务之外的,并且与测试任务相关的代码,用桩来代替,从而实现分离测试任务。例如函数A调用了函数B,函数B又调用了函数C和D,如果函数B用桩来代替,函数A就可以完全割断与函数C和D的关系(隔离了A和C、D,而不是隔离A和B)。

  • 补齐是指用桩来代替未实现的代码,例如,函数A调用了函数B,而函数B由其他程序员编写,且未实现,那么,可以用桩来代替函数B,使函数A能够运行并测试。补齐在并行开发中很常用。
  • 控制是指在测试时,人为设定相关代码的行为,使之符合测试需求

一、stub打桩

Stub可在gitHub上下载其源码,以源码方式集成,下载地址https://github.com/coolxv/cpp-stub ,提供的头文件名称为stub.h,需要注意的是,如果也引用Mockcpp方式,Mockcpp类也提供了stub头文件,所以需要在stub的stub.h头文件增加命名空间,防止两者冲突。

头文件提供两个函数,set,reset 函数

void set(T addr, S addr_stub)

addr为需要打桩的函数地址,addr_stub为需要替换的函数

void reset((T addr);

将被打桩的函数恢复。

(1)普通函数打桩(非static)
#include<iostream>
#include "stub.h"
using namespace std;
int foo(int a)
{   
    cout<<"I am foo"<<endl;
    return 0;
}
int foo_stub(int a)
{   
    cout<<"I am foo_stub"<<endl;
    return 0;
}
int main()
{
    Stub stub;
    stub.set(foo, foo_stub);
    foo(1);
    return 0;
}
(2)实例成员函数打桩
include<iostream>
#include "stub.h"
using namespace std;
class A{
    int i;
public:
    int foo(int a){
        cout<<"I am A_foo"<<endl;
        return 0;
    }
};
int foo_stub(void* obj, int a)
{   
    A* o= (A*)obj;
    cout<<"I am foo_stub"<<endl;
    return 0;
}
int main()
{
    Stub stub;
    stub.set(ADDR(A,foo), foo_stub);
    A a;
    a.foo(1);
    return 0;
}
例:重载函数打桩 (o_xpath_bool 为需要被打桩的函数)
namespace TinyXPath
{
? ?extern TIXML_API bool o_xpath_bool (const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr);
? ?extern TIXML_API bool o_xpath_bool (const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr, bool & o_res)
}
bool stub_o_xpath_bool (const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr)
{
? ? ?std::cout<<" stub_o_xpath_bool";
? ? return true;
}
TEST(SipXMLParserTest, ParseInfoArrived)
{
.....
bool (*func)(const TiXmlNode * XNp_source_tree, const char * cp_xpath_expr);
func= TinyXPath::o_xpath_bool;
mystub::Stub stub_path_bool;
stub_path_bool.set(func,stub_o_xpath_bool);
......
}

stub打桩在虚函数打桩比较麻烦,需要计算虚函数的地址

虚函数地址的计算:https://blog.csdn.net/qq_42956179/article/details/105428342

二、Mockcpp打桩

Stub因只要能获取到函数地址,即可对函数进行打桩,覆盖返回较广,但每一个打桩的函数需要写一个合适的替换函数,比较麻烦,且某些场景无法满足,如测试函数中多次调用到同一个需要打桩的函,如下例中,如采用stub方式对test打桩,针对test函数不同入参,难以编写一个合适的打桩函数。

int func()
{
int a = test(1);
int b = test(2);
return a + b;
}

Mockcpp 在C函数打桩相对于Stub函数打桩较为方便。

C函数与静态函数打桩

MOCKCPP对C函数及静态函数打桩采用MOCKER方式

(1)mock规范:每个MOCKER(function)开始,跟一系列的.stubs、.with、.will等的内容的整体,称为一个mock规范。

(2)核心关键字:指stubs/defaults/expects/before/with/after/will/then/id等这些直接跟在点后面的关键字。

(3)扩展关键字:指once()/eq()/check()/returnValue()/repeat()等这些作为核心关键字参数的关键字。(AMOCK的follow在mockcpp中是check)

例:MOCKER(UCSP::bIsDomain).stubs().will(returnValue(true));

类函数打桩

采用的是MockObject关键字,无法对普通成员函数打桩。

对虚函数打桩

例:

struct Base0
   {
      virtual int  base00() = 0;
      virtual bool base01(int) const = 0;
      virtual ~Base0() {}
   };
   struct Base1
   {
      virtual void base10() = 0;
      virtual long base11(const std::string&) const = 0;
      virtual int  base12() const = 0;
      virtual ~Base1() {}
   };
   struct Interface: public Base0, public Base1
   {
      virtual const std::string& a() {}
      virtual void b(bool) {}
   };
   void testShouldBeAbleReturnTheExpectedValue()
   {
       MockObject<Interface> mock;
       mock.method(&Interface::base00).stubs().will(returnValue(20));
       TS_ASSERT_EQUALS(20, mock->base00());
       TS_ASSERT_EQUALS(20, mock->base00());
       TS_ASSERT_EQUALS(20, mock->base00());
   }
   void testShouldBeAbleToSupportMultipleThenSpecification()
   {
       MockObject<Interface> mock;
       mock.method(&Interface::base00)
           .stubs()
           .will(returnValue(20))
           .then(returnValue(10))
           .then(returnValue(1))
           .then(returnValue(5));
       TS_ASSERT_EQUALS(20, mock->base00());
       TS_ASSERT_EQUALS(10, mock->base00());
       TS_ASSERT_EQUALS(1, mock->base00());
       TS_ASSERT_EQUALS(5, mock->base00());
       TS_ASSERT_EQUALS(5, mock->base00());
   }

调用GlobalMockObject::verify()释放

GitHub - ez8-co/emock: 🐞 下一代C/C++跨平台mock库 (Next generation cross-platform mock library for C/C++) github上还提及了一种方法 emock,打桩时发现有报错,未深究.

UT覆盖率统计

编译环境为linux,采用的QT平台

**编译命令: **

Pro文件中添加编译属性

QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage

QMAKE_LFLAGS+=-fprofile-arcs -ftest-coverage

另添加

QMAKE_CXXFLAGS +=-fno-inline

QMAKE_LFLAGS +=-fno-inline

这两行属性主要是禁止内联,可以获取内联函数地址,从而便于打桩

运行UT工程后会生成.gcna文件

统计覆盖率命令,

使用的是gcov工具

具体命令:

cd ../tmp

lcov -c -d ./ -o test.info --rc lcov_branch_coverage=1 --rc lcov_excl_br_line="new|delete|malloc|free|c_str|.value"

lcov -r test.info "/3rd/" -o MediaSDK.info --rc lcov_branch_coverage=1

/lcov -r test.info -o MediaSDK.info --rc lcov_branch_coverage=1

genhtml --branch-coverage MediaSDK.info --output-directory result

--rc lcov_excl_br_line参数表示去除不想要计算的分支关键字

lcov -r去除不想覆盖的文件目录

标签: 单元测试

本文转载自: https://blog.csdn.net/danfu_wu/article/details/124870607
版权归原作者 hao_hao先生 所有, 如有侵权,请联系我们删除。

“UT编写记录”的评论:

还没有评论