让我们先来检查一下一些不必要的代码。在 UnitTestsMain.cpp
文件中,有以下声明:
extern "C" int _ZN3Cam6myFuncEv();
是多余的。它只是告诉C++编译器,对那个原型函数的引用,其mangled名为_ZN3Cam6myFuncEv
,是对该名称的外部定义函数的引用。这与编译器已经从以下代码中获得的信息完全相同,只是表达方式不同:
namespace Cam {
int myFunc();
...
}
当包含CameraHandler.h
时,因为_ZN3Cam6myFuncEv()
是Cam::myFunc
的名称修饰形式。对Cam::myFunc
的extern "C"
重新声明没有害处,但对编译或链接也没有任何贡献。
接下来是主要问题:为什么在UnitTestsMain.cpp
中会调用您的模拟函数int __wrap__ZN3Cam6myFuncEv()
而不是int Cam::myFunc
?
int main(){
std::cout << Cam::myFunc() << std::endl;
std::cout << Cam::myFunc2() << std::endl;
return 0;
}
按照你的要求;但是你的模拟测试没有在CameraHandler.cpp
文件中调用int Cam::myFunc
函数:
int Cam::myFunc2(){
return Cam::myFunc() + 11;
}
答案在
链接器选项--wrap
文件中的说明中:
--wrap=symbol
使用符号的包装函数。对于任何对该符号的未定义引用,将解析为__wrap_symbol。对于任何对__real_symbol的未定义引用,将解析为symbol。
也许你读了它,但没有理解
未定义引用的重要性。
这意味着当有效应
--wrap=symbol
时,并且链接器将其应用于包含对
symbol
的
未定义引用的对象文件时,它将使用对
__wrap_symbol
的引用来替换它们,并且在该对象文件中,对
__real_symbol
的
未定义引用将被替换为
symbol
。
现在,在从
UnitTestsMain.cpp
编译而来的
UnitTestsMain.o
中,对
Cam::myFunc()
和
Cam::myFunc2()
的引用都是未定义的。这两个函数都定义在
CameraHandler.cpp
中,编译为
CameraHandler.o
。
因此,在链接
UnitTestsMain.o
时,
--wrap ZN3Cam6myFuncEv
将生效,并将调用
Cam::myFunc
(=
ZN3Cam6myFuncEv
)替换为调用
__wrap_ZN3Cam6myFuncEv
。对于
Cam::myFunc2()
(=
ZN3Cam7myFunc2Ev
)的调用没有使用包装,不受影响:它将被解析为在
CameraHandler.o
中找到的定义。
但是,在链接
CameraHandler.o
时,两个函数都被定义了,因此
--wrap
不起作用。当
Cam::myFunc2()
调用
Cam::myFunc()
时,它调用
ZN3Cam6myFuncEv
而不是
__wrap_ZN3Cam6myFuncEv
。
这就解释了为什么程序输出:
999
12
不是:
999
1010
你能让你的嘲弄按预期工作吗?
可以。您只需确保每次调用 Cam::myFunc
并且希望进行模拟的调用都编译为不包含(真实)定义 Cam::myFunc
的对象文件。显而易见的方法是在其自己的源文件中定义 Cam::myFunc
。以下是修正后的示例:
CameraHandler.h
#ifndef CAMERAHANDLER_H
#define CAMERAHANDLER_H
namespace Cam {
int myFunc();
int myFunc2();
}
#endif
CameraHandlerMock.h
#ifndef CAMERAHANDLERMOCK_H
#define CAMERAHANDLERMOCK_H
extern "C" {
int __wrap__ZN3Cam6myFuncEv();
}
#endif
CameraHandler_myFunc.cpp
#include "CameraHandler.h"
using namespace Cam;
int Cam::myFunc() {
return 1;
}
CameraHandler_myFunc2.cpp
#include "CameraHandler.h"
using namespace Cam;
int Cam::myFunc2(){
return Cam::myFunc() + 11;
}
CameraHandlerMock.cpp
#include "CameraHandlerMock.h"
int __wrap__ZN3Cam6myFuncEv() {
return 999;
}
UnitTestsMain.cpp
#include <iostream>
#include "CameraHandler.h"
#include "CameraHandlerMock.h"
int main(){
std::cout << Cam::myFunc() << std::endl;
std::cout << Cam::myFunc2() << std::endl;
return 0;
}
Makefile
SRCS := UnitTestsMain.cpp CameraHandler_myFunc.cpp \
CameraHandler_myFunc2.cpp CameraHandlerMock.cpp
OBJS := $(SRCS:.cpp=.o)
LDFLAGS := -Wl,--wrap,_ZN3Cam6myFuncEv
.PHONY: unitTests clean
unitTests: testsMain
testsMain: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^
UnitTestsMain: CameraHandler.h CameraHandlerMock.h
CameraHandler_Func.o CameraHandler_Func2.o: CameraHandler.h
CameraHandlerMock.o: CameraHandlerMock.h
clean:
rm -f $(OBJS) testsMain
在此示例的Makefile中,您的生产版本构建根本没有被考虑。
通过这种方式,测试构建的运行如下:
$ make
g++ -c -o UnitTestsMain.o UnitTestsMain.cpp
g++ -c -o CameraHandler_myFunc.o CameraHandler_myFunc.cpp
g++ -c -o CameraHandler_myFunc2.o CameraHandler_myFunc2.cpp
g++ -c -o CameraHandlerMock.o CameraHandlerMock.cpp
g++ -Wl,--wrap,_ZN3Cam6myFuncEv -o testsMain UnitTestsMain.o \
CameraHandler_myFunc.o CameraHandler_myFunc2.o CameraHandlerMock.o
而且testsMain
会按照你的期望进行操作:
$ ./testsMain
999
1010
如果你将CameraHandlerMock.cpp
重写为以下内容,你可以简化源文件和makefile:
extern "C" {
int __wrap__ZN3Cam6myFuncEv() {
return 999;
}
}
那么,您根本不需要模拟头文件
CameraHandlerMock.h
。
如果您有许多需要以这种低级方式进行模拟的功能,则为每个功能单独定义可能会变得乏味。您可能知道,有更高级别、框架支持的模拟选项,例如
googlemock,具有丰富的模拟功能,而不需要进行这种乏味的工作。但是,可以说,它们可能用更复杂的乏味代替它。