Setup and Use of Unit Test Environment gtest, mockcpp, LCOV

Posted by Random on Tue, 18 Jan 2022 23:41:51 +0100

Setup and Use of Unit Test Environment gtest, mockcpp, LCOV

This article is mainly used to record individual learning, and also to provide reference for those who have the same needs.
Example source github:https://github.com/904221150/gtest_sample

1. Introduction to the environment

The software tools used to set up the unit test environment in this paper are:

1.1 gtest

gtest is a cross-platform (Liunx, Mac OS X, Windows, Cygwin, Windows CE and
Symbian)C++ Unit Test Framework, released by google Inc. gtest is generated for writing C++ tests on different platforms. It provides rich assertions, fatal and non-fatal judgments, parameterization, death tests, and so on.

1.2 mockcpp

Mockcpp is a C/C++ oriented mock framework. It specifies (or simulates) the behavior of the function, checks the incoming parameters, sets the outgoing parameters, and specifies the return value of the function.

1.3 LCOV

lcov is a front-end graphical display tool for GCC test coverage. It collects multiple source files by
Code override information for rows, functions, and branches (the gcda, gcno files are generated after the program executes, the links above say) and the collected information is generated into HTML pages.

Generating HTML requires the use of the genhtml command as explained below.

2. Environment Building

2.1 gtest Environment Setup

git clone https://github.com/google/googletest
cd googletest/
mkdir mybuild
cd mybuild
cmake ../
make

sudo cp lib/libg*.a /usr/lib
sudo cp -rf ../googletest/include/gtest /usr/include/gtest
sudo cp -rf ../googlemock/include/gmock /usr/include/gmock

2.2 mockcpp environment setup

Mockcpp download link: http://code.google.com/p/mockcpp , based on the latest version 2.6

Modify build_install.sh file

#Original File
#!/bin/bash
# build and install

install_dir=/home/jelly/Programming/cpp-ut-project/cpp-ut-project/tools/mockcpp

function build() {
	mkdir $1 2>/dev/null
	cd $1
	cmake -DCMAKE_INSTALL_PREFIX=$install_dir $2
	make install
}

build ../../build_mockcpp_to_install ../mockcpp/mockcpp

cd ../mockcpp/mockcpp


#Modified
#!/bin/bash
# build and install

mkdir mockcpp

install_dir=../mockcpp

function build() {
	mkdir $1 2>/dev/null
	cd $1
	cmake -DCMAKE_INSTALL_PREFIX=$install_dir $2
	make install
}

build build ..

cd ../mockcpp

Execute Command

./build_install.sh
cd mockcpp
sudo cp lib/libmockcpp.a /usr/lib
sudo cp -rf include/mockcpp /usr/include/mockcpp

2.3 LCOV Environment Setup

Execute Command

git clone https://github.com/linux-test-project/lcov.git

cd lcov/

make install

3. Examples of unit tests

This example aims to unit test fun

File directory

|--gtest
|  |
|  +--gtest.cpp
|--inc
|  |
|  +--fun.h
|--result
|  |
|  +(Coverage results)
|--src
|  |
|  +--fun.c
|--app.c
|--makefile

app.c Apply the principal function

#include <stdio.h>
#include"fun.h"

int main()
{
    printf("fun return %d\n", fun(1));
    return 0;
}

fun.c

#include<stdio.h>
#include"fun.h"

int fun(int x)
{
    int i = 0;
    i = fun_inside(1);
    return i > 0;
}

int fun_inside(int x)
{
    printf("enter fun_inside\n");
    return x - 1;
}

fun.h

#ifndef __FUN_H__
#define __FUN_H__

int fun(int x);
int fun_inside(int x);

#endif

gtest.cpp unit test cases

#include<iostream>
#include<gtest/gtest.h>
#include<mockcpp/mockcpp.hpp>
#include"fun.h"


using namespace std;

class TestF : public testing::Test{
    public:
    virtual void SetUp()
    {
        printf("fun start test\n");
    }
    virtual void TearDown()
    {
        printf("fun end test\n");
    }
};

TEST_F(TestF, fun1)
{
    int ret = 0;
    MOCKER(fun_inside)
        .stubs() 
        .with(any())
        .will(returnValue(2));
    ret = fun(1);
    EXPECT_EQ(1, ret);
}

class FooEnvironment : public testing::Environment{
public:
    virtual void SetUp()
    {
        std::cout << "Foo FooEnvironment SetUP" << std::endl;
    }
    virtual void TearDown()
    {
        std::cout << "Foo FooEnvironment TearDown" << std::endl;
    }
};

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

The main test is in TEST_ In the F(TestF, fun1) function, TEST_F provides a function for gtest that performs unit tests. The first parameter, TestF, inherits test::Test, which aims to provide a generic initialization call function and a generic end call function for the unit test. The second parameter is the test name. The output can be seen in the output section below. EXPECT_EQ provides a comparison function for gtests, where the output of fun(1) is compared to 1.

For more information about gtest, consult the relevant information by yourself.

MOCKER provides a piling function for mockcpp, where fun_inside piling

.stubs()//unlimited number of piling function calls

.with(any())//means that the piling function can be called with any input

.will(returnValue(2);// Fun_ Inside returns 2 after being piled

More detailed use of mockcpp allows you to query relevant data yourself

Makefile (makefile is not written well here, temporary output is in the. / directory)

objdir = obj
srcdir = src

SRC_PATH = . \
	src
OBJ_PATH = obj
GTEST_PATH = gtest

SRC = $(foreach dir, $(SRC_PATH), $(wildcard $(dir)/*.c))
OBJ = $(patsubst $(srcdir)/%.c, $(objdir)/%.o, $(SRC))

SRC_GTEST_PATH = gtest \
	src
SRC_GTEST = $(foreach dir, $(SRC_GTEST_PATH), $(wildcard $(dir)/*.c))
SRC_GTEST += $(foreach dir, $(SRC_GTEST_PATH), $(wildcard $(dir)/*.cpp))

CC = gcc
CXX = g++
INCLUDES = -Iinc
LIBS = -lgtest \
    -lpthread \
    -lgtest_main \
	-lmockcpp
CFLAGS = -g -Wall -O0
CTESTFLAGS = -fprofile-arcs \
    -ftest-coverage

target = my_gtest

all : $(target)

my_app : $(SRC)
	@echo $(SRC)
	@echo $(OBJ)
	$(CC) $^ -o $@ $(INCLUDES) $(CFLAGS)

my_gtest : $(SRC_GTEST)
	@echo $(SRC_GTEST)
	$(CXX) $^ -o $@ $(CFLAGS) $(CTESTFLAGS) $(INCLUDES) $(LIBS)
	./my_gtest
	lcov -d . -t test -o test.info -b . -c
	genhtml -o result test.info

$(INCLUDES) $(LIBS)

clean:
	rm my_app

clean_test:
	rm my_gtest
	rm *.gcda
	rm *.gcno
	rm *.info
	rm -rf result/*

gtest runs with a link-lgtest-lpthread-lgtest_ Main
mockcpp runs with link-lmockcpp, code optimization is-O0
lcov requires gcov built in gcc to provide coverage information before traveling, and -fprofile-arcs-ftest-coverage is required

$(CXX) $^ -o $@ $(CFLAGS) $(CTESTFLAGS) $(INCLUDES) $(LIBS)
My_is generated after compilation GTEST execution file, and. gcno file

./my_gtest
A compiled unit test is executed and generated here. gcda file

lcov -d . -t test -o test.info -b . -c
-d: Store. gcda,. Path to gcno file
-t: Name of the target (optional)
-o: Generated coverage file. info
-b: The starting location of the relative directory
-c: capture, collection coverage

genhtml -o result test.info
Generate coverage report

Open result/index.html is viewable

Output Results

. /my_gtest output, where TEST_is run F (TestF, fun1)

. /my_app output, where you can compare how fun functions work

lcov coverage page, where src coverage is not high, due to fun. Fun_in C Inside function not unit tested

Appendix: Coverage Explanation

Code Coverage

After unit testing, we certainly want to be able to visually see what code our tests cover.
In theory, if we can cover 100% of all our code, we can say that our code is not bugged.
But in fact, 100% coverage is harder than you might think. For large projects, it's good to have 80% to 90% sentence coverage.

Coverage-related references: https://cloud.tencent.com/developer/article/1552518

Topics: unit testing