问题
和同事配合开发一个功能点。同事给了头文件给我,我这边开发不受阻塞,单测中mock他的接口就可以了。
有个函数A,原型是int A();但是后面他有更新分支了,改了原型变成了int A(bool arg);
那我功能代码里面肯定跟着改了,但是单测里面忘记改了。单测使用如下:
int mockA()
{
return 0;
}
xreplace((void *A, (void *mockA);
由于转了void*,编译时不会报错的。
实际上功能代码跑到A会调用mockA这个函数,参数其实是不同的。运行单测时,调用A(false),实际上会变成:
- 压栈false
- 调用call A,实际上会是call mockA
调用约定是__stdcall。
在Win32 Api中,由主调用函数采用从右到左的压栈方式进行参数压栈,然后由被调用者弹栈(pop),被调用者自己在退出时清空堆栈。
所以调用者压栈了false,但是mockA退出时候不会清理堆栈因为他认为自己是没有入参的。这样栈就不平衡了。
解决方法
首先肯定是把mockA原型改对。这样确实在win32下跑单测不会蹦了。
有啥更好的拓展吗,怎么预防后面再次出现类似问题?很自然想到单测mock应该限制函数类型一致。
优化mock方法,校验函数原型应该一样:
// 强制函数类型需要一样
#define cpp_xtest_replace(target, hook) \
do {\
if (!std::is_same<decltype(target), decltype(hook)>::value) {\
ASSERT_TRUE(0); \
}\
xtest_replace__((void*)target, (void*)hook, #target, #hook);\
} while (0);
使用:
cpp_xtest_replace(A, mockA);
问题
和同事配合开发一个功能点。同事给了头文件给我,我这边开发不受阻塞,单测中mock他的接口就可以了。
有个函数A,原型是int A();但是后面他有更新分支了,改了原型变成了int A(bool arg);
那我功能代码里面肯定跟着改了,但是单测里面忘记改了。单测使用如下:
由于转了
void*,编译时不会报错的。实际上功能代码跑到A会调用mockA这个函数,参数其实是不同的。运行单测时,调用A(false),实际上会变成:
调用约定是
__stdcall。在Win32 Api中,由主调用函数采用从右到左的压栈方式进行参数压栈,然后由被调用者弹栈(pop),被调用者自己在退出时清空堆栈。
所以调用者压栈了false,但是mockA退出时候不会清理堆栈因为他认为自己是没有入参的。这样栈就不平衡了。
解决方法
首先肯定是把mockA原型改对。这样确实在win32下跑单测不会蹦了。
有啥更好的拓展吗,怎么预防后面再次出现类似问题?很自然想到单测mock应该限制函数类型一致。
优化mock方法,校验函数原型应该一样:
使用:
cpp_xtest_replace(A, mockA);