单元测试,打桩问题

先说一下我的问题,一直在做的程序没有单元测试的代码,现在希望增加单元测试功能。考察了一些平台和技术,记录一下。

我的程序的结构如下

程序包含a,b两个dll,其中b依赖a。现在希望对b进行单元测试。

a.dll的示例代码如下

struct s
{
	int sa;
	int sb;
	int sc;
};
class a{
public:
	a(int i)
	{
	};
	void afun(s* ss)
	{
		ss->sc=ss->sa+ss->sb;
	};
	static a aa;
};
a a::aa(1);

b.dll的示例代码如下:


class b{
public:
	void bfun()
	{
		s sss;
		sss.sa=1;
		sss.sb=2;
		a::aa.afun(&sss);
		ret=sss.sc;
	}
	int ret;
};

由于现有的结构,测试代码不会修改原来的dll代码。而要调试b.dll,希望其中的sss返回不同的值来测试。这就需要对afun进行mock。预期的测试代码是这样的:

	mock(a.aa.afun);//mock afun函数,直接修改ss->sc的值为10	
        b bb;
	bb.bfun();
	EXPECT_EQ(10,bb.ret);

试了以下几种方案:

1.gmock

这个比较有名,跟gtest配套使用,但是却不能解决我的问题。gmock通过继承要mock的类,并且把新的类传递到测试类中。也就是要求a类中的afun必须是虚函数,而且b的函数中需要以a类为参数,这样就可以将mock的类传入。显然两条都不符合。

2.mockcpp

这个可以mock静态的c函数,还可以mock类的静态成员函数,但是还不知道怎么mock非静态函数。并且另一个问题,只能指定返回值,没法修改参数的值。

3.经过了漫长的搜索之后,终于发现了一个有用的技术。叫inline hook,通过直接在运行时修改函数的第一条指令跳转到新的函数执行。还找到了一个实用的代码,参考c++ 单元测试打桩技巧总结(stub、mock)

经过测试,实用这个库里的代码能够实现我要的效果。

同时,记录一下调试过程中出现的错误。

1.Run-Time Check Failure #0 错误

在使用中,出现了这个错误,然后程序就崩溃了。

经过搜索,并查看了汇编代码。发现了这个错误检查的用处。

这个错误是由于_RTC_CheckEsp_检查没通过。那么这个检查又是干嘛的呢。查看https://blog.csdn.net/wupangzi/article/details/7032356 

通过汇编代码可以看到原因。

在调用函数之前,会保持esp的值,调用函数返回之后,再查看一下esp的值,正常情况下应该保持不变,否则就会报这个错误。

那么为什么我的代码会出现不一致呢。仔细检查才发现,多了4个字节。原来我要替代的函数定义有两个参数,但是第二个参数时有默认值的,所以调用的时候只传入了一个参数,而我写的桩函数把这个给忘了,只写了一个参数。这就导致了调用时压栈两个,调用完了出栈一个,正好差了4。

这个错误也大部分是由于函数参数不一致导致的。

参考:

1.c++ 单元测试打桩技巧总结(stub、mock)   引用地址https://blog.csdn.net/coolxv_6533/article/details/79550197

Stub API 源码地址: https://github.com/coolxv/cpp-stub

说明:
- 只适用linux,和windows的x86、x64架构
- access private function相关方法基于C++11(参考:https://github.com/martong/access_private)
- replace function相关方法基于C++03
- windows和linux的用法会稍微不同,原因是获取不同类型函数地址的方法不同,且调用约定有时不一样

不可以打桩的情况:
- 不可以对exit函数打桩,编译器做了特殊优化
- 不可以对纯虚函数打桩,纯虚函数没有地址
- static声明的普通内部函数不能打桩,内部函数地址不可见(解析ELF或许可以获得函数地址)

。。。。。。

2.关于_RTC_CheckEsp_   引用地址https://blog.csdn.net/no_lock/article/details/46696095

今天工作时,反汇编中注意到_RTC_CheckEsp_这个函数,查了查资料,发觉这是个运行时检查函数,应该是用来检查缓冲区溢出的。

具体代码如下:

0135154B  int         3  
0135154C  int         3  
0135154D  int         3  
0135154E  int         3  
0135154F  int         3  
_RTC_CheckEsp:
01351550  jne         esperror (1351553h)  
01351552  ret  
esperror:
01351553  push        ebp  
01351554  mov         ebp,esp  

调用出现的位置有:

printf("input a number: ");
013513BE  mov         esi,esp  
013513C0  push        offset string "input a number: " (135575Ch)  
013513C5  call        dword ptr [__imp__printf (13582B8h)]  
013513CB  add         esp,4  
013513CE  cmp         esi,esp  
013513D0  call        @ILT+305(__RTC_CheckEsp) (1351136h)  
scanf("%d",&n);
013513D5  mov         esi,esp  
013513D7  lea         eax,[n]  
013513DA  push        eax  
013513DB  push        offset string "%d\n" (1355758h)  
013513E0  call        dword ptr [__imp__scanf (13582B4h)]  
013513E6  add         esp,8  
013513E9  cmp         esi,esp  
013513EB  call        @ILT+305(__RTC_CheckEsp) (1351136h)  

printf和scanf函数都会先保存esp至esi中,printf/scanf函数调用结束后,调用_RTC_CheckEsp检查值有没有发生改变。

函数结束ret过程中也有类似操作,不过是esp和ebp进行比较。
3浅析VS编译开关: /RTCc、/RTCu、/RTCs 引用地址https://blog.csdn.net/wupangzi/article/details/7032356

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值