玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

简介:

一、前言

在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况。我们之前的做法通常是写一个通用方法,然后编写在测试案例调用它。即使使用了通用方法,这样的工作也是有很多重复性的,程序员都懒,都希望能够少写代码,多复用代码。Google的程序员也一样,他们考虑到了这个问题,并且提供了一个灵活的参数化测试的方案。

二、旧的方案

为了对比,我还是把旧的方案提一下。首先我先把被测函数IsPrime帖过来(在gtest的example1.cc中),这个函数是用来判断传入的数值是否为质数的。

复制代码
//  Returns true iff n is a prime number.
bool  IsPrime( int  n)
{
    
//  Trivial case 1: small numbers
     if  (n  <=   1 return   false ;

    
//  Trivial case 2: even numbers
     if  (n  %   2   ==   0 return  n  ==   2 ;

    
//  Now, we have that n is odd and n >= 3.

    
//  Try to divide n by every odd number i, starting from 3
     for  ( int  i  =   3 ; ; i  +=   2 ) {
        
//  We only have to try i up to the squre root of n
         if  (i  >  n / i)  break ;

        
//  Now, we have i <= n/i < n.
        
//  If n is divisible by i, n is not prime.
         if  (n  %  i  ==   0 return   false ;
    }
    
//  n has no integer factor in the range (1, n), and thus is prime.
     return   true ;
}
复制代码

 

假如我要编写判断结果为True的测试案例,我需要传入一系列数值让函数IsPrime去判断是否为True(当然,即使传入再多值也无法确保函数正确,呵呵),因此我需要这样编写如下的测试案例:

复制代码
TEST(IsPrimeTest, HandleTrueReturn)
{
    EXPECT_TRUE(IsPrime(
3 ));
    EXPECT_TRUE(IsPrime(
5 ));
    EXPECT_TRUE(IsPrime(
11 ));
    EXPECT_TRUE(IsPrime(
23 ));
    EXPECT_TRUE(IsPrime(
17 ));
}
复制代码

 

我们注意到,在这个测试案例中,我至少复制粘贴了4次,假如参数有50个,100个,怎么办?同时,上面的写法产生的是1个测试案例,里面有5个检查点,假如我要把5个检查变成5个单独的案例,将会更加累人。

接下来,就来看看gtest是如何为我们解决这些问题的。

三、使用参数化后的方案

1. 告诉gtest你的参数类型是什么

你必须添加一个类,继承testing::TestWithParam<T>,其中T就是你需要参数化的参数类型,比如上面的例子,我需要参数化一个int型的参数

class  IsPrimeParamTest :  public ::testing::TestWithParam < int >
{

};

 

2. 告诉gtest你拿到参数的值后,具体做些什么样的测试

这里,我们要使用一个新的宏(嗯,挺兴奋的):TEST_P,关于这个"P"的含义,Google给出的答案非常幽默,就是说你可以理解为”parameterized" 或者 "pattern"。我更倾向于parameterized"的解释,呵呵。在TEST_P宏里,使用GetParam()获取当前的参数的具体值。

TEST_P(IsPrimeParamTest, HandleTrueReturn)
{
    
int  n  =   GetParam();
    EXPECT_TRUE(IsPrime(n));
}

 

嗯,非常的简洁!

3. 告诉gtest你想要测试的参数范围是什么

 使用INSTANTIATE_TEST_CASE_P这宏来告诉gtest你要测试的参数范围:

INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values( 3 5 11 23 17 ));

 

第一个参数是测试案例的前缀,可以任意取。

第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:IsPrimeParamTest

第三个参数是可以理解为参数生成器,上面的例子使用test::Values表示使用括号内的参数。Google提供了一系列的参数生成的函数:

Range(begin, end[, step]) 范围在begin~end之间,步长为step,不包括end
Values(v1, v2, ..., vN) v1,v2到vN的值
ValuesIn(container) and ValuesIn(begin, end) 从一个C类型的数组或是STL容器,或是迭代器中取值
Bool() false 和 true 两个值
Combine(g1, g2, ..., gN)

这个比较强悍,它将g1,g2,...gN进行排列组合,g1,g2,...gN本身是一个参数生成器,每次分别从g1,g2,..gN中各取出一个值,组合成一个元组(Tuple)作为一个参数。

说明:这个功能只在提供了<tr1/tuple>头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果你的系统确实支持,而gtest判断错误的话,你可以重新定义宏GTEST_HAS_TR1_TUPLE=1

 

四、参数化后的测试案例名

因为使用了参数化的方式执行案例,我非常想知道运行案例时,每个案例名称是如何命名的。我执行了上面的代码,输出如下:


从上面的框框中的案例名称大概能够看出案例的命名规则,对于需要了解每个案例的名称的我来说,这非常重要。 命名规则大概为:

prefix/test_case_name.test.name/index

五、类型参数化

gtest还提供了应付各种不同类型的数据时的方案,以及参数化类型的方案。我个人感觉这个方案有些复杂。首先要了解一下类型化测试,就用gtest里的例子了。

首先定义一个模版类,继承testing::Test:

复制代码
template  < typename T >
class  FooTest :  public  testing::Test {
 
public :
  
  typedef std::list
< T >  List;
  
static  T shared_;
  T value_;
};
复制代码

 

接着我们定义需要测试到的具体数据类型,比如下面定义了需要测试char,int和unsigned int :

typedef testing::Types < char int , unsigned  int >  MyTypes;
TYPED_TEST_CASE(FooTest, MyTypes);

 

又是一个新的宏,来完成我们的测试案例,在声明模版的数据类型时,使用TypeParam

复制代码
TYPED_TEST(FooTest, DoesBlah) {
  
//  Inside a test, refer to the special name TypeParam to get the type
  
//  parameter.  Since we are inside a derived class template, C++ requires
  
//  us to visit the members of FooTest via 'this'.
  TypeParam n  =   this -> value_;

  
//  To visit static members of the fixture, add the 'TestFixture::'
  
//  prefix.
  n  +=  TestFixture::shared_;

  
//  To refer to typedefs in the fixture, add the 'typename TestFixture::'
  
//  prefix.  The 'typename' is required to satisfy the compiler.
  typename TestFixture::List values;
  values.push_back(n);
  
}
复制代码

上面的例子看上去也像是类型的参数化,但是还不够灵活,因为需要事先知道类型的列表。gtest还提供一种更加灵活的类型参数化的方式,允许你在完成测试的逻辑代码之后再去考虑需要参数化的类型列表,并且还可以重复的使用这个类型列表。下面也是官方的例子:

复制代码
template  < typename T >
class  FooTest :  public  testing::Test {
  
};

TYPED_TEST_CASE_P(FooTest);
复制代码

 

接着又是一个新的宏TYPED_TEST_P类完成我们的测试案例:

复制代码
TYPED_TEST_P(FooTest, DoesBlah) {
  
//  Inside a test, refer to TypeParam to get the type parameter.
  TypeParam n  =   0 ;
  
}

TYPED_TEST_P(FooTest, HasPropertyA) {  }
复制代码

接着,我们需要我们上面的案例,使用REGISTER_TYPED_TEST_CASE_P宏,第一个参数是testcase的名称,后面的参数是test的名称

REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);

接着指定需要的类型列表:

typedef testing::Types < char int , unsigned  int >  MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);

这种方案相比之前的方案提供更加好的灵活度,当然,框架越灵活,复杂度也会随之增加。

六、总结

gtest为我们提供的参数化测试的功能给我们的测试带来了极大的方便,使得我们可以写更少更优美的代码,完成多种参数类型的测试案例。

系列链接:

1.玩转Google开源C++单元测试框架Google Test系列(gtest)之一 - 初识gtest

2.玩转Google开源C++单元测试框架Google Test系列(gtest)之二 - 断言

3.玩转Google开源C++单元测试框架Google Test系列(gtest)之三 - 事件机制

4.玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

5.玩转Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试

6.玩转Google开源C++单元测试框架Google Test系列(gtest)之六 - 运行参数

7.玩转Google开源C++单元测试框架Google Test系列(gtest)之七 - 深入解析gtest

8.玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架

 

 

本文转自CoderZh博客园博客,原文链接:http://www.cnblogs.com/coderzh/archive/2009/04/08/1431297.html,如需转载请自行联系原作者

相关文章
|
10天前
|
敏捷开发 Java 测试技术
探索软件测试中的自动化测试框架
在软件开发的生命周期中,软件测试扮演着至关重要的角色。随着技术的不断进步和软件项目的日益复杂化,传统的手动测试方法已经无法满足高效、准确的测试需求。自动化测试作为一种提高测试效率和质量的有效手段,越来越受到开发者和测试者的青睐。本文将深入探讨自动化测试框架的重要性、常见的自动化测试工具以及如何选择合适的自动化测试框架。
35 10
|
12天前
|
设计模式 前端开发 JavaScript
自动化测试框架设计原则与最佳实践####
本文深入探讨了构建高效、可维护的自动化测试框架的核心原则与策略,旨在为软件测试工程师提供一套系统性的方法指南。通过分析常见误区,结合行业案例,阐述了如何根据项目特性定制自动化策略,优化测试流程,提升测试覆盖率与执行效率。 ####
38 6
|
12天前
|
人工智能 前端开发 测试技术
探索软件测试中的自动化框架选择与优化策略####
本文深入剖析了当前主流的自动化测试框架,通过对比分析各自的优势、局限性及适用场景,为读者提供了一套系统性的选择与优化指南。文章首先概述了自动化测试的重要性及其在软件开发生命周期中的位置,接着逐一探讨了Selenium、Appium、Cypress等热门框架的特点,并通过实际案例展示了如何根据项目需求灵活选用与配置框架,以提升测试效率和质量。最后,文章还分享了若干最佳实践和未来趋势预测,旨在帮助测试工程师更好地应对复杂多变的测试环境。 ####
38 4
|
12天前
|
监控 jenkins 测试技术
自动化测试框架的构建与实践
【10月更文挑战第40天】在软件开发周期中,测试环节扮演着至关重要的角色。本文将引导你了解如何构建一个高效的自动化测试框架,并深入探讨其设计原则、实现方法及维护策略。通过实际代码示例和清晰的步骤说明,我们将一起探索如何确保软件质量,同时提升开发效率。
29 1
|
7天前
|
JavaScript 安全 编译器
TypeScript 与 Jest 测试框架的结合使用,从 TypeScript 的测试需求出发,介绍了 Jest 的特点及其与 TypeScript 结合的优势,详细讲解了基本测试步骤、常见测试场景及异步操作测试方法
本文深入探讨了 TypeScript 与 Jest 测试框架的结合使用,从 TypeScript 的测试需求出发,介绍了 Jest 的特点及其与 TypeScript 结合的优势,详细讲解了基本测试步骤、常见测试场景及异步操作测试方法,并通过实际案例展示了其在项目中的应用效果,旨在提升代码质量和开发效率。
25 6
|
3天前
|
机器学习/深度学习 人工智能 Java
探索软件测试中的自动化框架选择与优化策略####
本文深入探讨了在软件测试领域,面对众多自动化测试框架时,如何根据项目特性、团队技能及长远规划做出最佳选择,并进一步阐述了优化这些框架以提升测试效率与质量的策略。通过对比分析主流自动化测试框架的优劣,结合具体案例,本文旨在为测试团队提供一套实用的框架选型与优化指南。 ####
|
3天前
|
敏捷开发 前端开发 Java
软件测试中的自动化测试框架选择与实践
在当今软件开发生命周期中,自动化测试已成为提升软件质量和开发效率的关键手段。本文旨在探讨自动化测试框架的选择标准及其在实际项目中的应用实践。通过对主流自动化测试框架的分析比较,结合具体案例,本文将阐述如何根据项目需求和团队特点选择合适的自动化测试工具,并分享实施过程中的经验教训。
10 1
|
16天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
50 3
|
2月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
67 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
3月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
250 7
Jmeter实现WebSocket协议的接口测试方法
下一篇
无影云桌面