关于后端:CICD实战服务自动测试

34次阅读

共计 8451 个字符,预计需要花费 22 分钟才能阅读完成。

导语

随着微服务、容器、云计算的倒退,近些年 DevOps、CI/CD 等概念越来越多地映入大家的眼帘。许多开发团队都心愿利用这些理念来进步软件品质和开发效率,工欲善其事必先利其器,什么样的工具才可能满足开发者的需要?TARS 作为一套优良的开源微服务开发经营一体化平台,领有多语言、高性能、麻利研发、高可用等特点。那么 TARS 是否可能完满反对 DevOps 理念呢?在上一篇文章中,咱们理解了如何将开源 CI 工具 Jenkins 与 TARS 集成实现 TARS 服务的自动化构建与部署。而软件测试是软件开发过程中必不可少的一步,本文将在上一篇文章的根底上,以一次残缺的实际来展现如何通过 Jenkins 与 TARS 集成实现 TARS 服务的自动化单元测试。

目录

  • 什么是单元测试
  • 环境筹备
  • 批改我的项目
  • 建设测试项目
  • 批改 Jenkins 我的项目配置
  • 自动化测试

什么是单元测试

随着微服务、容器、云计算的倒退,近些年 DevOps、CI/CD 等概念越来越多地映入大家的眼帘。DevOps 是当初风行的一种软件开发办法,将继续开发、继续测试、继续集成、继续部署、继续监控等贯通到软件开发的生命周期中,用于进步软件的开发品质,被以后简直所有顶级公司采纳。

TARS 作为一套优良的开源微服务开发经营一体化平台,领有多语言、高性能、麻利研发、高可用等特点。通过将开源 CI 工具 Jenkins 与 TARS 集成即可实现针对 TARS 服务开发的自动化测试,加重开发与测试人员的工作量。因为篇幅所限,本文仅针对自动化单元测试开展。

软件测试是软件开发过程中必不可少的一步,而单元测试是软件测试中最根底的一种模式。单元测试中,单元能够指代码中的一个模块、一个函数或者一个类;单元测试就是为每个单元编写测试用例,对该单元进行正确性测验,测试逻辑是否正确,确保每个单元的行为合乎预期。因而单元测试的增加可能很大水平上升高软件或服务上线后呈现问题的概率。

环境筹备

本文基于前文应用的 TarsCppCIDemo 我的项目,应用 GoogleTest 作为单元测试框架,理论我的项目中请依据需要抉择测试框架。

装置 GoogleTest

GoogleTest 是 Google 开源的一套 C++ 测试框架,可能很不便的进行单元测试。接下来,咱们在部署 Jenkins 的机器上装置这个框架。

GoogleTest 的 GitHub 仓库地址为: https://github.com/google/googletest,能够间接 clone 后构建装置。这里咱们装置稳定版,在 GitHub 页面右侧点击 Release 能够查看历史公布的版本。本文截稿前最新版本为 1.10.0,下载安装命令如下


wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz

tar -zxvf release-1.10.0.tar.gz

cd googletest-release-1.10.0

mkdir build

cd build

cmake ..

make

make install

至此,GoogleTest 便装置实现了。

装置 xUnit plugin

xUnit 是一个 Jenkins 平台的插件,能够用于读取单元测试的后果,反对多种测试框架,包含 GoogleTest。

关上 Jenkins 的治理页面,进入 系统管理 -> 插件治理 -> 可选插件 ,在搜素框中搜寻 xUnit,在呈现的后果中抉择 xUnit plugin,点击 间接装置 后,期待 Jenkins 装置重启即可。

批改我的项目

当初回到咱们之前创立的 Demo 我的项目,咱们为我的项目的 HelloServer 增加几个接口和一个计数类,实现一个简略的计数服务。

批改 Hello.tars

服务的接口通过 tars 文件定义,咱们编辑 Hello.tars,为其增加三个接口,别离为减少计数、缩小计数和获取以后的计数值,编辑后的 Hello.tars 如下


module TarsCppCIDemo

{

interface Hello

{int test();

int increment(out int count);

int decrement(out int count);

int getCount(out int count);

};

};

能够看出,除了主动生成的 test 接口,咱们增加了 increment, decrement, getCount 三个接口,三个接口均返回 count,即计数的后果。在我的项目根目录 TarsCppCIDemo 中,进入 HelloServer/src 目录,咱们运行脚本 tars2cppHello.tars 转化为相应的头文件 Hello.h


cd HelloServer/src

/usr/local/tars/cpp/tools/tars2cpp Hello.tars

批改 HelloImp.h

而后咱们编辑接口实现文件的头文件 HelloImp.h,在类 HelloImp 中增加三个接口的申明:


/**

* @param count out 返回计数值

* @return 服务状态码

*/

virtual int increment(int& count, tars::TarsCurrentPtr current);

/**

* @param count out 返回计数值

* @return 服务状态码

*/

virtual int decrement(int& count, tars::TarsCurrentPtr current);

/**

* @param count out 返回计数值

* @return 服务状态码

*/

virtual int getCount(int& count, tars::TarsCurrentPtr current);

新建 Counter 类

接下来咱们通过新建 Counter 类来实现计数器的性能。TarsCpp 的公共组件中提供了单件类模板组件 TC_Singleton,咱们间接继承该类,Counter.h 如下:


#ifndef __COUNTER_H_

#define __COUNTER_H_

#include "util/tc_singleton.h"

#include "util/tc_thread.h"

#include "util/tc_thread_rwlock.h"

// A simple monotonic counter.

class Counter: public tars::TC_Singleton<Counter> {

private:

int counter_;

tars::TC_ThreadRWLocker rwlocker_;

public:

// Creates a counter that starts at 0.

Counter() : counter_(0) {}

// 返回计数值,并对计数执行 +1

int Increment();

// 返回计数值,并对计数执行 -1

int Decrement();

// 返回以后计数值

int GetCount();};

#endif // __COUNTER_H_

其中 TC_ThreadRWLocker 为 TarsCpp 工具组件提供的读写锁类,更多 TarsCpp 公共组件能够在 TarsCpp/util/include/util 中查看其定义和用法。

接下来是 Counter.cpp


#include "Counter.h"

int Counter::Increment() {tars::TC_ThreadWLock wlock(rwlocker_);

return counter_++;

}

int Counter::Decrement() {if (counter_ == 0) { // 为 0 时间接返回

tars::TC_ThreadRLock rlock(rwlocker_);

return counter_;

} else {tars::TC_ThreadWLock wlock(rwlocker_);

return counter_--;

}

}

int Counter::GetCount() {tars::TC_ThreadRLock rlock(rwlocker_);

return counter_;

}

这样就实现 Counter 类的创立了。

批改 HelloImp.cpp

接下来咱们增加对三个接口的实现


#include "HelloImp.h"

#include "servant/Application.h"

#include "Counter.h"

using namespace std;

void HelloImp::initialize() {}

void HelloImp::destroy() {}

int HelloImp::increment(int& count, tars::TarsCurrentPtr current) {count = Counter::getInstance()->Increment();

return 0;

}

int HelloImp::decrement(int& count, tars::TarsCurrentPtr current) {count = Counter::getInstance()->Decrement();

return 0;

}

int HelloImp::getCount(int& count, tars::TarsCurrentPtr current) {count = Counter::getInstance()->GetCount();

return 0;

}

到这里,咱们就实现了三个接口逻辑的增加。

建设测试项目

接下来咱们创立测试项目,在 HelloServer 目录下新建 test 目录,并在 test 中新建 app_ut.cppCMakeLists.txt,目录构造如下


HelloServer

├── build

├── CMakeLists.txt

├── src

│   ├── CMakeLists.txt

│   ├── Counter.cpp

│   ├── Counter.h

│   ├── Hello.h

│   ├── HelloImp.cpp

│   ├── HelloImp.h

│   ├── HelloServer.cpp

│   ├── HelloServer.h

│   └── Hello.tars

└── test

├── app_ut.cpp

└── CMakeLists.txt

增加测试用例

GoogleTest 蕴含了丰盛的断言,可能不便的进行单元测试,对于 GoogleTest 的应用办法能够浏览其应用文档。

咱们在 app_ut.cpp 中增加测试流程和测试用例,如下,为 Counter 类增加了三个测试用例,测试的执行程序是依照定义程序执行的。其中的 EXPECT_EQ 是用于判断两个值是否相等的断言,不相等触发谬误,输入在测试后果中。


#define private public

#include "gtest/gtest.h"

#include "Counter.h"

namespace {// Tests the Increment() method.

TEST(Counter, Increment) {Counter* c = Counter::getInstance();

EXPECT_EQ(0, c->Increment());

EXPECT_EQ(1, c->Increment());

EXPECT_EQ(2, c->Increment());

EXPECT_EQ(3, c->Increment());

c->counter_ = 0;

}

// Tests the Decrement() method.

TEST(Counter, Decrement) {Counter* c = Counter::getInstance();

EXPECT_EQ(0, c->Decrement());

EXPECT_EQ(0, c->Increment());

EXPECT_EQ(1, c->Increment());

EXPECT_EQ(2, c->Decrement());

EXPECT_EQ(1, c->Decrement());

c->counter_ = 0;

}

// Tests the GetCount() method.

TEST(Counter, GetCount) {Counter* c = Counter::getInstance();

EXPECT_EQ(0, c->GetCount());

EXPECT_EQ(0, c->Increment());

EXPECT_EQ(1, c->GetCount());

EXPECT_EQ(1, c->Increment());

EXPECT_EQ(2, c->GetCount());

EXPECT_EQ(2, c->Decrement());

EXPECT_EQ(1, c->GetCount());

c->counter_ = 0;

}

} // namespace

其中的 #define private public 是单元测试中罕用到的宏替换,不便批改公有对象进行测试。Counter 类是单件类,为了不影响其余测试用例,每个测试用例最初将 counter_ 置零。

为测试用例增加 CMakeLists.txt

实现了测试用例的创立,咱们须要编译测试项目,生成用于测试的可执行文件。编译框架能够依据本人的偏好抉择,本例子中咱们应用 cmake 治理代码编译,对于 cmake 的用法能够参照官网文档。

首先,咱们在 test 目录下增加 CMakeLists.txt 文件,内容如下。


cmake_minimum_required(VERSION 3.10)

find_package(GTest REQUIRED)

set(TARS_INC "/usr/local/tars/cpp/include")

set(TARS_LIB "/usr/local/tars/cpp/lib")

set(TARS_LIB_UTIL "${TARS_LIB}/libtarsutil.a")

set(COUNTER_SRC "${PROJECT_SOURCE_DIR}/src/Counter.cpp")

include_directories(${GTEST_INCLUDE_DIRS}

${TARS_INC}

${PROJECT_SOURCE_DIR}/src )

link_directories(${TARS_LIB})

add_executable(app_ut ${COUNTER_SRC} app_ut.cpp)

target_link_libraries( app_ut

/usr/local/tars/cpp/lib/libtarsutil.a

${GTEST_BOTH_LIBRARIES}

pthread )

gtest_discover_tests(app_ut

XML_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/build/test_result" )

cmake 中在 3.10 之后的版本中增加了对 gtest 的反对,新增了gtest_discover_tests 间接增加测试,但理论应用过程中发现该办法的 XML_OUTPUT_DIR 参数在 3.18 版本才起作用,低于 3.18 的版本都无奈在指定门路生成测试后果文件。

因而倡议 cmake 版本在 3.18 以下的设施上,通过执行构建的测试可执行文件进行测试用例的运行,在后续局部中会进行具体介绍。

批改我的项目主 CMakeLists.txt

在应用 TarsCpp 我的项目生成工具生成我的项目的时候,默认生成了用于我的项目编译的 CMakeLists.txt。接下来咱们将批改这个文件,实现在构建我的项目的同时,编译测试用例。

在上节中咱们曾经实现了测试用例局部的 CMakeLists.txt 的编写,在我的项目主 CMakeLists.txt 文件中,只有增加子目录即可,如下,新增了 enable_testing()add_subdirectory(test)


cmake_minimum_required(VERSION 2.8.8)

project(Demo-DemoServer)

option(TARS_MYSQL "option for mysql" ON)

option(TARS_SSL "option for ssl" OFF)

option(TARS_HTTP2 "option for http2" OFF)

if(WIN32)

include (c:tarscppmakefiletars-tools.cmake)

else()

include (/usr/local/tars/cpp/makefile/tars-tools.cmake)

endif()

####you can: cd build; cmake .. -DTARS_WEB_HOST={your web host}

set(TARS_WEB_HOST ""CACHE STRING"set web host")

IF (TARS_WEB_HOST STREQUAL "")

set(TARS_WEB_HOST "http://tars.test.com")

ENDIF ()

include_directories(/usr/local/tars/cpp/thirdparty/include)

link_directories(/usr/local/tars/cpp/thirdparty/lib)

#include_directories(/home/tarsprotol/App/OtherServer)

enable_testing() # 开启测试

add_subdirectory(src)

add_subdirectory(test) # 增加 test

#target_link_libraries(mysqlclient ssl crypto nghttp2-static)

接下来依照 TarsCpp 我的项目的编译形式编译构建我的项目就能够了。

运行测试用例

有两种运行测试用例的形式,依据要求任选一种即可

  • 间接应用 cmake 集成的测试性能,构建实现后只须要在 build 目录下间接执行 make test 即可,要求 cmake 版本为 3.18
  • 运行测试用例编译构建的可执行文件,在执行完我的项目构建命令后,会在 build/bin 中生成测试用例可执行文件,在本我的项目中为 app_ut,间接执行即可,实用于 cmake 2.8.8 以上版本。通常会增加参数 --gtest_output="xml:test*.xml" 用于输入测试后果,如下

./bin/app_ut --gtest_output="xml:testresults.xml"

批改 Jenkins 我的项目配置

本局部将会介绍如何配置 Jenkins 工作,实现可能主动执行我的项目中的单元测试,并获取测试的后果。

批改构建 shell 命令

构建过程的脚本中,咱们只须要增加命令运行测试用例即可,依据上节中的 运行测试用例 局部,依据 cmake 版本抉择任一命令即可,以执行测试用例可执行文件为例,批改后的构建脚本如下


#!/bin/sh

mkdir -p HelloServer/build

cd HelloServer/build

cmake ..

make -j4

make HelloServer-tar

./bin/app_ut --gtest_output="xml:test_results.xml"

增加构建后操作

点击 减少构建后操作步骤 抉择 Publish xUnit test result report,新增构建后步骤,如下

而后在 Report Type 点击 新增,抉择 GoogleTest

而后在 Pattern 中填写匹配模式,用于匹配后面构建过程中生成的 xml 文件,能够间接应用模式 **/*.xml 匹配所有的 xml 文件,也能够依据命名形式自定义模式匹配,如下

最初点击 保留 就实现了 Jenkins 工作的配置。

自动化测试

后面咱们曾经实现了自动化测试所需的配置,同自动化构建与部署一样,接下来只须要 push 我的项目到 GitHub 仓库即可触发自动化构建与测试流程。

等构建实现后,咱们能够查看此次构建的测试后果,如下

到这里,咱们就实现了基于 Jenkins 的 TarsCpp 我的项目自动化单元测试,其余语言大致相同,抉择本人相熟的测试框架和 Jenkins 上对应的插件即可。

总结

本文在前一篇文章的根底上,介绍了如何通过 Jenkins 与 TARS 集成,实现 TARS 服务的自动化单元测试,帮忙晋升软件开发过程中的软件品质。

TARS 能够在思考到易用性和高性能的同时疾速构建零碎并主动生成代码,帮忙开发人员和企业以微服务的形式疾速构建本人稳固牢靠的分布式应用,从而令开发人员只关注业务逻辑,进步经营效率。多语言、麻利研发、高可用和高效经营的个性使 TARS 成为企业级产品。

TARS 微服务助您数字化转型,欢送拜访:

TARS 官网:https://TarsCloud.org

TARS 源码:https://github.com/TarsCloud

Linux 基金会官网微服务收费课程:https://www.edx.org/course/bu…

获取《TARS 官网培训电子书》:https://wj.qq.com/s2/6570357/…

或扫码获取:

正文完
 0