UT preparation record

UT writing rules https://zhuanlan.zhihu.com/p/424117483

1. Follow the AIR principle

A (automation): unit tests should be fully automated and non interactive.

I (Independence): unit test cases must not call each other or depend on the order of execution.

--------Counterexample: method2 needs to rely on the execution of method1 and take the execution result as the input of method2.

R (Repeatable): unit tests can be executed repeatedly and cannot be affected by the external environment.

2. Structure

a. A new file directory is recommended for unit test code

Note: this directory will be skipped during source code construction, while the unit test framework scans this directory by default.

b. The package and structure of test cases need to be consistent with the package and structure being tested.

3. Naming

a. Test class naming specification: tested business + test, tested interface + test, tested class + test

b. Test case naming specification: test case naming should be concise, comprehensive and descriptive. Avoid using names that have no meaning in test1 and test2. Secondly, necessary function method annotations are needed.

4. No use

a. Do not use thread in unit tests sleep

Encapsulation of UT test class

The difference between Setup TearDown SetUpTestCase and teardown testcase in GTEST_ Cheng Yaojin's blog CSDN blog_ gtest teardown

Use test_ A firmware class needs to be created before F, which inherits from:: testing::Test class. Use public or protected to describe its members inside the class. In order to ensure that the actual test subclass can use its member variables. In the constructor or the SetUp method inherited from:: testing::Test class, we can implement the data we need to construct. In the destructor or TearDown method inherited from:: testing::Test class, you can implement some code for resource release. There is no unified standard for the selection of "constructor / destructor" and "SetUp/TearDown". Generally speaking, don't do anything taboo in the constructor / destructor, such as throwing exceptions

overall situation:

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;
}

Test suite:

Header file:

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

Piling method

The main purposes of piling are: isolation, supplement and control. Isolation refers to separating test tasks from product projects so that they can be compiled, linked and run independently. The basic method of isolation is pile driving, which replaces the code outside the test task and related to the test task with pile, so as to separate the test task. For example, function A calls function B, and function B calls functions C and D. if function B is replaced by piles, function A can completely cut off the relationship with functions C and D (isolating A and C, D, rather than A and b).

  • Complementation refers to replacing unimplemented code with piles. For example, if function A calls function B and function B is written by other programmers and is not implemented, piles can be used to replace function B so that function A can run and test. Complement is often used in parallel development.

  • Control refers to the behavior of artificially setting relevant codes during testing to make them meet the test requirements

1, stub piling

Stub can download its source code on gitHub, integrate in the form of source code, and download the address https://github.com/coolxv/cpp-stub , the provided header file name is stub h. It should be noted that if the Mockcpp method is also referenced, the Mockcpp class also provides a stub header file, so it needs to be in the stub H header files add namespaces to prevent conflicts between the two.

The header file provides two functions, set and reset

void set(T addr, S addr_stub)

Addr is the address of the function to be used, addr_stub is the function that needs to be replaced

void reset((T addr);

Restore the function to be driven.

(1)Ordinary function (non-linear) 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)Instance member function
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;
}
Example of heavy load piling function (o_xpath_bool Is a function that needs to be driven)
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 piling is troublesome in virtual function piling, and the address of virtual function needs to be calculated

Calculation of virtual function address: https://blog.csdn.net/qq_42956179/article/details/105428342

2, Mockcpp piling

As long as the function address can be obtained, stub can pile the function, covering a wide range of returns. However, it is troublesome to write an appropriate replacement function for each pile driving function, and some scenarios cannot be met. For example, the test function calls the same pile driving function for many times. In the following example, if test is driven by stub, it is difficult to write an appropriate pile driving function for different parameters of test function.

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

The pile driving of Mockcpp in C function is more convenient than that of Stub function.

C function and static function

MOCKCPP adopts MOCKER method for C function and static function piling

(1)mock specification: each MOCKER(function) starts with a series of stubs,. with,. The whole content of will is called a mock specification.

(2) Core Keywords: these keywords that directly follow the dot, such as stubs / defaults / expectations / before / with / after / will / then / ID.

(3) Extended keyword: refers to keywords such as once()/eq()/check()/returnValue()/repeat() as core keyword parameters. (AMOCK's follow is check in mockcpp)

Example: mocker (UCSP:: bisdomain) stubs(). will(returnValue(true));

Class function piling

The MockObject keyword is used, and ordinary member functions cannot be modified.

Pile driving for virtual function

Example:

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());
   }

Call GlobalMockObject::verify() to release

GitHub - ez8-co/emock: 🐞 Next generation cross platform mock library for C/C + + A method called emock was also mentioned on github, which was reported as wrong during piling and was not studied in depth

UT coverage statistics

The compilation environment is linux and QT platform is adopted

Compile command:

Add compilation attributes to Pro files

QMAKE_CXXFLAGS  += -fprofile-arcs -ftest-coverage

QMAKE_LFLAGS+=-fprofile-arcs -ftest-coverage


Add another

QMAKE_CXXFLAGS +=-fno-inline

QMAKE_LFLAGS +=-fno-inline

These two lines of attributes are mainly used to prohibit inlining. You can obtain the address of the inlined function for convenience

After running the UT project, it will be generated gcna file

Statistical coverage command,

gcov tool is used

Specific command:

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_ The line parameter indicates that the branch keyword that you do not want to calculate is removed

lcov -r removes files and directories that you do not want to overwrite

Tags: unit testing

Posted by Lauj on Fri, 20 May 2022 01:52:04 +0300