Python代码覆盖工具coverage.py介绍

最近是跟代码覆盖干上了,今天下午测试一个功能的时候,路过另一段代码,发现一个问题,由此想到既然C++都要搞代码覆盖,为什么不搞搞python的呢?很容易就找到了coverage.py ,这个工具比较简单,我用easy_install安装的,非常顺利。由于python不需要编译链接,所以这个工具使用非常简单。coverage run [options] your_cmd [cmd options]。

假如原来的运行的命令是:

fact_compare.py -d result

需要收集代码覆盖信息的话只需要这样运行

coverage run –branch fact_compare.py -d result

运行完了以后会在当前目录下生成一个.coverage文件,保存了代码覆盖信息,可以用简单的coverage report看来简单的结果,当然,有更好的html结果显示

coverage html -d your_result_folder

最左边是绿,也就是没有颜色的代码的就是完全覆盖(其实只是语句覆盖和分支覆盖),黄色的是部分分支覆盖,红色的语句覆盖都不是。

解决gcov不能生成.gcda文件,以及其他错误

上一篇博客简单介绍了如何用lcov获取代码覆盖信息,今天回到公司动手做,还是遇到了些问题。

跟开发沟通了一下,我们的程序不是在每台开发机上都能成功编译的,因为需要很多第三方库,等等……所以就登陆到他的开发机上把程序编译好,然后拷贝到我自己的机器上运行。问题就出在这里,编译的时候在机器A上,路径是/home/qli/debug,我在机器B上运行的目录是 /home/jchen/work/,程序运行完毕生成.gcda文件失败,出现若干个提示如下:

profiling:/home/qli:Cannot create directory
profiling:/home/qli/debug/server/Selection.pb.gcda:Skip
在我的运行环境中没有相应的目录,所以不能生成.gcda文件,Google了一下发现,只要设一下GCOV_PREFIX和GCOV_PREFIX_STRIP这两个环境变量就好了。GCOV_PREFIX就是制定生成数据文件的前缀,GCOV_PREFIX_STRIP就是需要在原来的路径上去掉多少层目录,这2个变量配合使用就能把数据文件生成到我们想要的地方。假如我编译时候的路径是“/home/qli/debug/server/”,我现在的运行路径是“/home/jchen/work/ads/ads_server/server/”,那么执行如下命令即可
export GCOV_PREFIX=”/home/jchen/work/ads/ads_server/server/”
export GCOV_PREFIX_STRIP=4
执行完这两行命令,就可以开始运行被测程序,然后正常退出,就会看到.gcda文件生成出来了。
.gcda文件生成了就可以用lcov直接处理它们,但是我在处理的过程中遇到以下错误:“stamp mismatch with graph file”
Processing Ads_Request.gcda
/home/jchen/work/ads/ads_server/server/xxx.gcda:stamp mismatch with graph file
geninfo: WARNING: gcov did not create any files for /home/jchen/work/ads/ads_server/server/xxx.gcda!
导致出现这个错误的原因是.gcda和.gcno文件并不是同一次build出来的,它们2个文件的时间戳就不一样了,很简单,更新所有的.gcno文件即可。

C/C++代码覆盖工具gcov与lcov入门

gcov是一个可用于C/C++的代码覆盖工具,是gcc的内建工具。下面介绍一下如何利用gcov来收集代码覆盖信息。
想要用gcov收集代码覆盖信息,需要在gcc编译代码的时候加上这2个选项 “-fprofile-arcs -ftest-coverage”,把这个简单的程序编译一下

gcc -fprofile-arcs -ftest-coverage hello.c -o hello

编译后会得到一个可执行文件hello和hello.gcno文件,当用gcc编译文件的时候,如果带有“-ftest-coverage”参数,就会生成这个.gcno文件,它包含了程序块和行号等信息
接下来可以运行这个hello的程序

./hello 5
./hello 12

运行结束以后会生成一个hello.gcda文件,如果一个可执行文件带有“-fprofile-arcs”参数编译出来,并且运行过至少一次,就会生成。这个文件包含了程序基本块跳转的信息。接下来可以用gcov生成代码覆盖信息:

gcov hello.c

运行结束以后会生成2个文件hello.c.gcov和myfunc.c.gcov。打开看里面的信息:

-: 0:Source:myfunc.c
-: 0:Graph:hello.gcno
-: 0:Data:hello.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include
-: 2:
-: 3:void test(int count)
1: 4:{
-: 5: int i;
10: 6: for (i = 1; i < count; i++)
-: 7: {
9: 8: if (i % 3 == 0)
3: 9: printf (“%d is divisible by 3 \n”, i);
9: 10: if (i % 11 == 0)
#####: 11: printf (“%d is divisible by 11 \n”, i);
9: 12: if (i % 13 == 0)
#####: 13: printf (“%d is divisible by 13 \n”, i);
-: 14: }
1: 15:}

被标记为#####的代码行就是没有被执行过的,代码覆盖的信息是正确的,但是让人去读这些文字,实在是一个杯具。不用担心,有另外一个工具叫lcov,可以用程序解析这些晦涩的字符,最终输出成html格式的报告,很好吧!

lcov -d . -t ‘Hello test’ -o ‘hello_test.info’ -b . -c

指定lcov在当前目录“.”去找代码覆盖的信息,输出为’hello_test.info’ ,这个hello_test.info是一个中间结果,需要把它用genhtml来处理一下,genhtml是lcov里面的一个工具。

genhtml -o result hello_test.info

指定输出目录是 result。一个完整的html报告就生成了,做一个连接,把这个目录连到随便一个web server的目录下,就可以看报告了。

Continue reading “C/C++代码覆盖工具gcov与lcov入门”

测试代码重构实例

Martin Fowler的《重构》这本书基本上每个程序员都会看,对于做单元测试的测试工程师来说,测试的代码本身也是程序,也需要重构。最近在看《XUnit Test Patterns》,把以前的做的东西重新梳理了一下,并且落实到新的项目中。

首先来看看一个最原始的单元测试代码:

[TestMethod]
public void GetPaymentAccountByOwnerID()
{
	int ownerId = 1300100000;
	//Delete the account
	TestHelper.DeletePaymentAccountByOwnerId(ownerId);

	//Here should be create an account
	PaymentAccount paymentAccount = PaymentGateway.PaymentProvider.GetPaymentAccountByOwnerID(ownerId, AccountOwnerType.NormalUser);

	//Verify the payment account instance
	Assert.IsTrue(paymentAccount.AccountID > 0);
	Assert.AreEqual(DateTime.Now.DayOfYear, paymentAccount.CreateTime.DayOfYear);
	Assert.AreEqual(DateTime.Now.DayOfYear, paymentAccount.UpdateTime.DayOfYear);
	Assert.AreEqual(0, paymentAccount.Balance, 0.0001);
	Assert.AreEqual(0, paymentAccount.AvailableBalance, 0.0001);
	Assert.AreEqual(0, paymentAccount.FreezeAccount, 0.0001);
}

以上是个很简单的单元测试代码,应用了AAA原则,首先做好准备(删掉原有的数据),然后执行测试,最后再验证返回结果。看起来很好也很清晰。首先发现一个问题,就是那一堆Assert让人觉得迷惑,究竟想干啥?其实那6句Assert都是验证程序返回的PaymentAccount对象是是否符合设计。我把这些Assert语句提取出来,作为一个方法,让测试代码更加容易让人明白。也就有了版本2。
Continue reading “测试代码重构实例”

单元测试中三种准备Test Fixture的方法比较

首先说一下Test Fixture,我不知道怎么样翻译这个Test Fixture,没能搜到一个翻译的比较合适的。最让我气愤的是某人翻译的一本书中,直接把Test Fixture翻译成为测试夹具,这明显就是什么词霸词典硬翻译出来的,我强烈鄙视这样不负责任的翻译行为。

The test fixture is everything we need to have in place to exercise the SUT

我觉得这是一个对Test Fixture的一个很清晰明了的定义,就是运行被测软件所需要的一切东西,这个“东西”不单只是数据,同时还包括对被测软件的准备,例如实例化某个被测方法所在的类,准备数据库的ConnectionString等。通常来说,有三种方法来准备Test Fixture。

1. 内联方式:这种方式就是直接在测试方法中编写准备Test Fixture的代码。用这种方法的缺点是很容易造成代码的重复,出现很多复制粘贴的代码。同时,如果这个SETUP的过程比较复杂,也会降低测试代码的可读性,可维护性。另外的一个问题就是,这种方法很容易会带来测试数据Hard code的隐患。既然有那么多缺点,这种方法还有什么生命力呢?首先,可能对于初学者来说,这种方法是最简单的;其次,在一些只需要准备简单的Test Fixture的场合中,这种方法还是给编写测试的人提供了便利。
Continue reading “单元测试中三种准备Test Fixture的方法比较”

控制反转有助于提高程序的可测试性

控制反转,英文是Inversion of Control,简称IoC。这个概念在JAVA的Spring框架中比较常见,在.NET的开发中,Ioc或者其的变种依赖注入DI(Dependency Injection)是不太常见的。一般来说,类A要使用类B,类A会在其内部对类B进行控制,比较典型的做法就是在类A中创建一个类B的实例;而控制反转,就是把一个已经创建好的类B实例交给类A去控制。
例子:
Continue reading “控制反转有助于提高程序的可测试性”

自动化白盒测试工具PEX应用实例 – 发现ZUNE死机的BUG

在12月的时候,我在博客里面介绍了一个自动化单元测试工具–PEX。在上一篇博客中,又讲述了微软ZUNE在闰年最后一天死机的原因。现在把这两篇文章串起来,跟大家分享一下如何用PEX帮助改善代码的质量。

首先来看一下用PEX测试ZUNE处理日期的方法的结果,如图:


Continue reading “自动化白盒测试工具PEX应用实例 – 发现ZUNE死机的BUG”

在C#单元测试中使用HttpContext的简单解决办法

场景:最近在测试一个.NET的Http Module,这个Module是用来做URL重写的。刚开始进展的比较顺利,因为该Module里面的方法参数基本上都是String,后来这个Module进行了一下重构,所有参数都变成了HttpContext了,这就直接导致原来的单元测试都跑不起来了,接着就开始了弄HttpContext了。

1. 采用Visual Studio自带的ASP.NET单元测试

刚开始我看了一下被测试的代码,虽然说用到了HttpContext,但是有很多地方我都可以绕过去的,意思就是这个HttpContext只是名以上需要的一个参数,只要它不是NULL就可以了,并不影响我的测试,所以我采用了ASP.NET Unit Test的办法来获取一个HttpContext,这个方法实现起来是最简单的,但是会有一些问题,后面会提及到。
Continue reading “在C#单元测试中使用HttpContext的简单解决办法”

单元测试中的常用测试模式

单元测试跟软件设计一样,有一些常用的模式,这篇文章是介绍一些常用的模式,其中的示例是C#代码,都比较简单,我想大家都能看懂。下面进入正题:

1. 准备,执行,断言(Arrange, Act, Assert)。这种模式是非常常见的,套用这种模式进行单元测试通常的做法如下:

  1. 准备测试环境,测试数据等
  2. 执行被测试方法
  3. 用断言来验证执行结果

下面是一段测试代码,被测方法的功能是把字符串中每个单词的首字母转为大写,特殊字符用下划线替代。
Continue reading “单元测试中的常用测试模式”

PEX-.NET自动化白盒测试工具的介绍(1)

PEX的全称是(Program EXploration),是一款在.NET下可应用的自动化白盒测试工具,来自于微软研究院。PEX通过分析代码来自动生成测试用例。对于程序里面的每一行代码,PEX都会尽可能地生成合适的输入值来达到提高覆盖率的目标。同时PEX还会分析代码中的分支,生成覆盖更多分支的测试代码(输入数据);PEX在执行代码的同时会监控和分析代码的控制流和数据流,了解程序的行为。每运行完单一个测试以后,PEX会选择一条在前面的测试中没有覆盖到的路径,并且尝试执行它。这一切都是约束求解算法来实现的,官方文档中提到的是一个叫Z3的约束求解器。因为PEX了解被测代码内部结构和行为,所以它不是一个简单地输入随机参数的黑盒测试工具。PEX不会尝试枚举所有可能的输入,事实上也不可能枚举完所有的可能输入。
Continue reading “PEX-.NET自动化白盒测试工具的介绍(1)”