1. 程式人生 > >溫故而知新:gtest單元測試工具和lcov覆蓋率統計工具的結合使用

溫故而知新:gtest單元測試工具和lcov覆蓋率統計工具的結合使用

一、簡介  

     之所以叫溫故而知新,是因為將這兩個工具結合起來作為單元測試工具的想法在上一個專案中應用了,好像還沒有人將這兩種工具結合使用,或者沒有寫成部落格供大家參考,現在重新溫習下將想法寫下來。

    gtest單元測試工具接觸過的人都很熟悉了,它是一款google提供的強大的測試框架,測試案例的編寫也比較簡單,gtest案例的編寫可以參考系列博文:http://www.cnblogs.com/coderzh/archive/2009/03/31/1426758.html

    lcov程式碼覆蓋率統計工具,是gcov的延伸版本,提供程式實際執行的資訊(統計某行程式碼被執行的次數),其基於HTML的輸出通過瀏覽器以清晰的圖表形式呈現覆蓋率統計結果。locv相關詳細介紹可以參考博文:https://my.oschina.net/alphajay/blog/33725

二、gtest環境的搭建步驟:

(1)下載原始碼包搭建:

參考博文:http://www.linuxidc.com/Linux/2015-05/116894.htm。

我是按照這篇部落格的步驟下載原始碼將多餘的目錄刪除最後在gtest_tool目錄下只剩下兩個核心程式碼目錄:


(2)直接輸入命令安裝: sudo apt-get install libgtest-dev

三、lcov工具的安裝:

(1)下載原始碼包:http://ltp.sourceforge.net/coverage/lcov.php

(2)解壓:tar xvzf lcov-1.11.tar.gz

(3)cd lcov-1.11

(4)如果是交叉編譯移植到實機上需要執行這步:

修改檔案:lcov-1.11/bin/genifo
                    vim lcov-1.11/bin/genifo
然後將第65行的:our $gcov_tool = "gcov" 改為自己的交叉編譯器的gcov

比如我的交叉編譯工具是/usr/local/arm/4.3.2/bin/arm-linux-gcc
那麼就改為:our $gcov_tool = "/usr/local/arm/4.3.2/bin/arm-linux-gcov"

可以使用:find / -name *gcov來查詢下自己的交叉編譯工具在什麼目錄下

(5)sudo make install

注:除了下載原始碼包還可以執行下面兩條命令安裝:

           sudo apt-get install lcov

           sudo apt-get install ggcov

四、將兩者結合使用例項

環境搭建好後可以開心得玩耍了 哈哈

(1)首先了解下我需要測試的程式碼模型:

說明:這是我自己寫的簡單的訊息傳遞及監聽模型測試程式碼。messageModelLib目錄是訊息傳遞模型的核心程式碼,

編譯出來一個.so庫供該目錄的其他模組使用。messageModelLib目錄內容如下:


(2)重點是gtest_lcov目錄,該目錄是專門用來進行單元測試的,目錄內容如下:

說明:gtest_tool 為gtest原始碼的刪減版,lcov-1.11為lcov原始碼包編譯後的目錄,lcov_out為自己建的目錄用來存放lcov工具統計出的結果的輸出。test_case.c為編寫的測試用例,內容如下:

// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// A sample program demonstrating using Google C++ testing framework.
//
// Author: [email protected] (Zhanyong Wan)


// This sample shows how to write a simple unit test for a function,
// using Google C++ testing framework.
//
// Writing a unit test using Google C++ testing framework is easy as 1-2-3:


// Step 1. Include necessary header files such that the stuff your
// test logic needs is declared.
//
// Don't forget gtest.h, which declares the testing framework.

#include <limits.h>
#include "gtest/gtest.h"

#include "myComDef.h"
#include "responser12.h"
#include "listener12.h"

extern "C" {
	#include "log_out.h"
}



// Step 2. Use the TEST macro to define your tests.
//
// TEST has two parameters: the test case name and the test name.
// After using the macro, you should define your test logic between a
// pair of braces.  You can use a bunch of macros to indicate the
// success or failure of a test.  EXPECT_TRUE and EXPECT_EQ are
// examples of such macros.  For a complete list, see gtest.h.
//
// <TechnicalDetails>
//
// In Google Test, tests are grouped into test cases.  This is how we
// keep test code organized.  You should put logically related tests
// into the same test case.
//
// The test case name and the test name should both be valid C++
// identifiers.  And you should not use underscore (_) in the names.
//
// Google Test guarantees that each test you define is run exactly
// once, but it makes no guarantee on the order the tests are
// executed.  Therefore, you should write your tests in such a way
// that their results don't depend on their order.
//
// </TechnicalDetails>


// Tests Factorial().

// Tests factorial of negative numbers.

static responser1 res1_instance;
static responser2 res2_instance;

static listener1 lis1_instance;
static listener2 lis2_instance;


TEST(apl_registResponserTest, isTrue)
{
	ASSERT_EQ(TRUE, apl_registResponser(RES1, RES_MID, &res1_instance));
       ASSERT_EQ(TRUE, apl_registResponser(RES2, RES_HIGH, &res2_instance));
}


TEST(apl_registListenerTest, isTrue)
{
    ASSERT_EQ(TRUE, apl_registListener(RES1, &res1_instance, &lis1_instance));
    ASSERT_EQ(TRUE, apl_registListener(RES2, &res2_instance, &lis2_instance));
}


TEST(loop_send_test, isRight)
{
    int n = 0;
    while(n < 10)
    {
        MSG_INFO msgInfo;
        char msgData[128];

        msgInfo.resType = RES1;  //設定動作物件
        msgInfo.msg.eventID = R1_FUN1; //設定應該做什麼動作
        EXPECT_EQ(TRUE, apl_sendMessage(RES1, R1_FUN1, msgData)); //傳送開始訊息及動作引數

        //apl_unRegistResponser(RES1);

        msgInfo.msg.eventID = R1_FUN2;
        int a = 10, b = 122;
        apl_msgPacker(msgData,sizeof(int), &a,sizeof(int), &b, -1);
        EXPECT_EQ(TRUE, apl_sendMessage(RES1, R1_FUN2, msgData));

        msgInfo.resType = RES2;
        msgInfo.msg.eventID = R2_FUN1;
        double c = 4.23, d = 2.32;
        apl_msgPacker(msgData,sizeof(double), &c,sizeof(double), &d, -1);
        EXPECT_EQ(TRUE, apl_sendMessage(RES2, R2_FUN1, msgData));

        msgInfo.msg.eventID = R2_FUN2;
        a = 20;
        b = 30;
        apl_msgPacker(msgData,sizeof(int), &a,sizeof(int), &b, -1);
        EXPECT_EQ(TRUE, apl_sendMessage(RES2, R2_FUN2, msgData));

        sleep(1);
        ++n;
    }
}


// Step 3. Call RUN_ALL_TESTS() in main().
//
// We do this by linking in src/gtest_main.cc file, which consists of
// a main() function which calls RUN_ALL_TESTS() for us.
//
// This runs all the tests you've defined, prints the result, and
// returns 0 if successful, or 1 otherwise.
//
// Did you notice that we didn't register the tests?  The
// RUN_ALL_TESTS() macro magically knows about all the tests we
// defined.  Isn't this convenient?

主要測試三個case,訊息迴應者、對應監聽器的註冊和訊息的傳遞和監聽。

編寫好test_case.c以後檔案以後,關鍵的關鍵就是makefile的編寫了,我的makefile是gtest原始碼包example測試makefile基礎上修改的,內容如下:

# A sample Makefile for building Google Test and using it in user
# tests.  Please tweak it to suit your environment and project.  You
# may want to move it to your project's root directory.
#
# SYNOPSIS:
#
#   make [all]  - makes everything.
#   make TARGET - makes the given target.
#   make clean  - removes all files generated by make.

# Please tweak the following variable definitions as needed by your
# project, except GTEST_HEADERS, which you can use in your own targets
# but shouldn't modify.

# Points to the root of Google Test, relative to where this file is.
# Remember to tweak this if you move this file.
GTEST_DIR = ./gtest_tool  #gtest 原始碼所在目錄
XX = g++
CC = gcc
# Where to find user code.
USER_DIR = ..  #測試程式碼所在目錄

# Flags passed to the preprocessor.
# Set Google Test's header directory as a system directory, such that
# the compiler doesn't generate warnings in Google Test headers.
CPPFLAGS += -isystem $(GTEST_DIR)/include

# Flags passed to the C++ compiler.
CXXFLAGS += -g -Wall -Wextra -pthread
CXXFLAGS2 += -g -Wall -Wextra -pthread -fprofile-arcs -ftest-coverage #多了兩個編譯選項
# All tests produced by this Makefile.  Remember to add new tests you
# created to the list.
TARGET = appMain

# the link library you should change according to your need
LINK_LIB = -L$(USER_DIR)/lib -lSendMsgModel -lpthread -lrt

# All Google Test headers.  Usually you shouldn't change this
# definition.
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
                $(GTEST_DIR)/include/gtest/internal/*.h

# House-keeping build targets.

all : $(TARGET)

# Builds gtest.a and gtest_main.a.

# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)

# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized.  This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.
gtest-all.o : $(GTEST_SRCS_)
    g++ $(CPPFLAGS) -I$(GTEST_DIR) -c \
            $(GTEST_DIR)/src/gtest-all.cc

gtest_main.o : $(GTEST_SRCS_)
    g++ $(CPPFLAGS) -I$(GTEST_DIR) -c \
            $(GTEST_DIR)/src/gtest_main.cc

gtest.a : gtest-all.o
    $(AR) $(ARFLAGS) [email protected] $^

gtest_main.a : gtest-all.o gtest_main.o
    $(AR) $(ARFLAGS) [email protected] $^

# Builds a sample test.  A test should link with either gtest.a or
# gtest_main.a, depending on whether it defines its own main()
# function.
    
XX_SOURCES = $(wildcard $(USER_DIR)/myListeners/*.cpp $(USER_DIR)/myResponsers/*.cpp)
CC_SOURCES = $(wildcard $(USER_DIR)/logout/*.c)

XX_OBJECTS = $(patsubst %.cpp,%.o,$(XX_SOURCES))
CC_OBJECTS = $(patsubst %.c,%.o,$(CC_SOURCES))

INCLUDE_DIRS = -I$(USER_DIR)/include -I$(USER_DIR)/myListeners -I$(USER_DIR)/myResponsers -I$(USER_DIR)/logout

TEST_CASE_O = ./test_case.o #gtest 測試案例

 $(TEST_CASE_O) : %.o : %.c
    $(XX) -c $(CPPFLAGS) $< -o [email protected] $(INCLUDE_DIRS)
    
$(CC_OBJECTS) : %.o : %.c
    $(CC) -c $(CXXFLAGS2) $< -o [email protected] $(INCLUDE_DIRS)  #需要用lcov檢視哪個檔案的程式碼覆蓋率,編譯的時候就加上-fprofile-arcs -ftest-coverage編譯選項

$(XX_OBJECTS) : %.o : %.cpp
    $(XX) -c $(CXXFLAGS2) $< -o [email protected] $(INCLUDE_DIRS)

$(TARGET) : $(XX_OBJECTS) $(CC_OBJECTS) $(TEST_CASE_O) gtest_main.a
    $(XX) $(CXXFLAGS2) $^ -o [email protected] $(LINK_LIB)

#刪除程式碼目錄的 *.gcda,*.gcno和*.o檔案
SUBDIRS = $(USER_DIR)/myListeners $(USER_DIR)/myResponsers $(USER_DIR)/logout
GCDA_FILES = $(foreach dir, $(SUBDIRS), $(wildcard $(dir)/*.gcda))
GCNO_FILES = $(foreach dir, $(SUBDIRS), $(wildcard $(dir)/*.gcno))
OBJS_FILES = $(foreach dir, $(SUBDIRS), $(wildcard $(dir)/*.o))

clean :
    rm -rf $(TARGET) gtest.a gtest_main.a *.o *.gcno *.gcda *.info lcov_out/* \
    $(GCDA_FILES) $(GCNO_FILES) $(OBJS_FILES)



執行完後也會在生成gcon檔案的目錄下生成gcda檔案。


(6)最後把測試的命令(1~5)命令寫到run.sh腳本里,執行./run.sh就ok了。

#! /bin/bash
cd ..
make clean
cd ./gtest_lcov
make clean
sudo rm -rf ./lcov_out/*
make
./appMain

./lcov-1.11/bin/lcov -d ../ -t 'appMain' -o'appMain.info' -b . -c
./lcov-1.11/bin/genhtml appMain.info --quiet --output-directory lcov_out--title "appMain"
firefox ./lcov_out/index.html

(7)覆蓋率統計圖表:



至此,gtest單元測試工具和lcov覆蓋率統計工具的結合使用介紹完畢,共同學習進步。


相關推薦

故而gtest單元測試工具lcov覆蓋率統計工具結合使用

一、簡介        之所以叫溫故而知新,是因為將這兩個工具結合起來作為單元測試工具的想法在上一個專案中應用了,好像還沒有人將這兩種工具結合使用,或者沒有寫成部落格供大家參考,現在重新溫習下將想法寫下來。     gtest單元測試工具接觸過的人都很熟悉了,它是一款go

故而柯里化 與 bind()的認知

  什麼是柯里化?科裡化是把一個多引數函式轉化為一個巢狀的一元函式的過程。(簡單的說就是將函式的引數,變為多次入參) const curry = (fn, ...args) => fn.length <=&n

C# 故而Stream篇(三)

本文轉載連線: http://www.cnblogs.com/JimmyZheng/archive/2012/03/25/2416841.htmlTextWriter 和 StreamWriter目錄:為何介紹TextWriter?就像上篇講述的一樣,對於重要的基礎技術,我們

故而IUS、IFS、IFFS

INDEX SCANIFS: 索引全掃描這裏要註意的一點就是不需要掃描所有branch的分支塊,因為葉子塊與葉子塊之間有雙向指針,所以找到最左邊的葉子塊,就可以依序掃描。所以這裏可以有 兩個結論:1 索引鍵值是排序的,不需要額外對索引鍵值再一次order 2 通常情況下依序讀取則是單塊讀,因為多塊離散讀,你

故而java事務

date操作 dst 語句 port 分割 數據庫引擎 再次 lec con 一、什麽是Java事務 通常的觀念認為,事務僅與數據庫相關。 事務必須服從ISO/IEC所制定的ACID原則。ACID是原子性(atomicity)、一致性(consistency

故而(java實現)單例模式的七種寫法

反序 防止 代碼 工作 html 我想 變種 evel 才會 第一種(懶漢,線程不安全): Java代碼 public class Singleton { private static Singleton instance; private S

C#故而——訪問修飾符

組合 protected 訪問修飾符 當前 net 程序 reference csharp internal C#包含四種訪問修飾符以及一種組合修飾符: public:訪問不受限制。 protected:訪問僅限於包含類或從包含類派生的類型。 internal:訪問僅限於當

Android群英傳筆記——摘要,概述,的出發點,故而,能夠為師矣!

ram hide 基本 pretty 工具 log 透明 scroll 好的 Android群英傳筆記——摘要。概述,新的出發點,溫故而知新。能夠為師矣! 當工作的越久,就越感到力不從心了,基礎和理解才是最重要的,所以買了兩本書,醫生的《An

故而-String類

true 一次 sci 能夠 start 匹配 第一個 pop substring String不算是一種類型,而算是一個類。就是說String不僅能夠表示string類型,另一些自帶的方法能夠調用。溫故而知新。如今給大家總結了String類應該註意的地方。 (1

故而---淺析三層架構(一個超簡單的系統登錄三層架構實例)

lda code windows comm 面向 box reader 業務 兩個 剛開始接觸三層架構是在快兩個月前,那時候找了好多例子感覺也都看不怎麽懂,今天閑著沒事,就把以前學的東西翻出來,算是溫習溫習。由於本人也接觸時間不長,所以以下言論有不正確之處,多多

【C# in depth 第三版】故而(1) (轉)

64位 icm stringbu 值傳遞 關於 ota 現在 函數變量 出發 聲明 本文歡迎轉載,原文地址:http://www.cnblogs.com/DjlNet/p/7192354.html 前言 關於這本書(《深入理解C# 第三版》)的詳細

Spring故而 - bean的裝配(續)

metadata test BE 狀態 放心 cati 如果 需要 efi 按條件裝配bean 就是當滿足特定的條件時Spring容器才創建Bean,Spring中通過@Conditional註解來實現條件化配置bean package com.sl.ioc; imp

Spring故而 – AOP代理

for 過程 orm machine create 輸出 scac finally mac AOP的概念 AOP:Aspect-Oriented Programming(面向切面編程),維基百科的解釋如下:Aspect是一種新的模塊化機制,用來描述分散在對象、類或者函數中的

Spring MVC故而 – 從零開始

resolve 它的 完成後 web框架 攔截器 加載 class lns 目錄 Spring MVC簡介 Spring MVC是一款基於MVC架構模式的輕量級Web框架,目的是將Web開發模塊化,對整體架構進行解耦。 Spring MVC有一下優點: 作為Spr

故而 js 的錯誤處理機制

HA 機制 dom 處理 process test web ucc 報錯 // 在函數塊的try中return,會直接成為函數的return值 function test() { try { alrt(123) return ‘su

Spring MVC故而 – 參數綁定、轉發與重定向、異常處理、攔截器

單獨 UC exclude require 加載 pre buffered nts 節點 請求參數綁定 當用戶發送請求時,根據Spring MVC的請求處理流程,前端控制器會請求處理器映射器返回一個處理器,然後請求處理器適配器之心相應的處理器,此時處理器映射器會調用Spr

故而-PHP文件操作函數

單位 contents 結果 fgets HP 提示 文件句柄 etc 取整 1 文件操作流程 打開文件-》讀取或者寫入文件-》關閉文件 fopen->fread,fwrite->fclose fopen可以打開ftp或者http協議的文件,前提示對方支持 ph

web前端開發故而(二)

b前端開發 溫故而知新 行數 row 溫故 tab padding ram 列合並 2.0:帶機構的的表格分為<thead></thead> <tbody></tbody> </tfoot><

web前端開發故而(三)

隱藏 area for value 隱藏域 span selected orm web前端開發 3.1:input屬性hidden隱藏域 例:<input type="hidden" name="hidden" value="這是一個隱藏域" /> 3.2:

故而 Ajax 的坑 dataType: 'json'

ucc too 引號 似的 字符串 bsp 輸出 osc json數據 為了方便實驗,我隨便捏造了一個json數據,然後放在php中輸出。 請求明明是200,json數據也正確,但ajax就是不執行success回調? 原因是 dataType: ‘json‘, 導致的