[英]How do I use google mock in C?
我正在维护一个用 C 编写的遗留项目,用 C++ 编译器运行它是不可行的。 由于代码是交叉编译的,因此可以在主机环境中运行单元测试或类似测试。 因此也可以与 C++ 主机编译器连接并使用 google-test 和 google-mock。
google-mock 的某些功能似乎非常适合用于测试,例如调用实际实现和设置调用期望。
我希望能够在 C 代码中使用它们。 我可以看到确实可以在不使用 vtables 的情况下使用 google-mock,但是它需要模板。
有没有办法用谷歌模拟来模拟裸 C 函数?
编辑:
我基本上不得不使用谷歌模拟,但我认为虽然其他人会阅读这个线程比我有更好的灵活性。
我找到了一种能够在 google-mock 中模拟裸 C 函数的方法。
解决方案是将foobar
声明为映射到foobarImpl
的弱别名。 在生产代码中,您不实现foobar()
并且对于单元测试,您提供一个调用静态模拟对象的实现。
此解决方案是特定于 GCC 的,但还有其他编译器/链接器提供弱别名。
void foobar();
void foobarImpl();
foobar
添加一个属性,例如: void foobar() __attribute__((weak, alias("foobarImpl") ));
因此:
#pragma once
void foobar();
变成
// header.h
#pragma once
void foobar();
void foobarImpl(); // real implementation
和
extern "C" {
#include "header.h"
}
// code.c
void foobarImpl() {
/* do sth */
}
void foobar() __attribute__(( weak, alias ("foobarImpl") )); // declare foobar to be a weak alias of foobarImpl
这将告诉GNU接头与连接呼叫foobar()
与foobarImpl()
时没有符号叫做foobar()
然后添加测试代码
struct FooInterface {
virtual ~FooInterface() {}
virtual void invokeFoo() const { }
};
class MockFoo : public FooInterface {
public:
MOCK_CONST_METHOD0(invokeFoo, void());
}
struct RealFoo : public FooInterface {
virtual ~RealFoo() {}
virtual void invokeFoo() const { foobarImpl(); }
};
MockFoo mockFoo;
RealFoo realFoo;
void foobar() {
mockFoo.invokeFoo();
}
如果此代码已编译并链接,它将用模拟调用替换foobar
。 如果你真的想调用foobar()
你仍然可以添加一个默认调用。
ON_CALL(mockFoo, invokeFoo())
.WillByDefault(Invoke(&realFoo,&RealFoo::invokeFoo));
来自 Google Mock 常见问题解答:
我的代码调用静态/全局函数。 我可以嘲笑它吗?
你可以,但你需要做一些改变。一般来说,如果您发现自己需要模拟一个静态函数,则表明您的模块耦合太紧(灵活性较差、可重用性较差、可测试性较差等)。 您可能最好定义一个小接口并通过该接口调用该函数,然后可以轻松模拟。 最初需要一些工作,但通常很快就会收回成本。
这篇 Google 测试博客文章说得很好。 一探究竟。
您的问题特别提到了 Google Mock,但没有说明使用该框架的任何其他原因。 另一个答案建议使用一种看似不必要的侵入性解决方法。
因此,我希望我可以提出一个可以很好地工作的替代建议,而不必使用弱别名等。
我已经使用 CppUTest ( https://cpputest.github.io/ ) 进行单元测试和模拟,成功地在几个大型的 C 项目(一些 C++)上成功。 无需借助上述任何诡计即可进行嘲讽。
不幸的是,项目文档有点薄弱,书中有一些更好的(如果有点敏捷教义)信息和示例(也可以作为 PDF 分发)“嵌入式 C 的测试驱动开发”- James W Greening(ISBN-13: 978-1-934356-62-3)
我意识到这是一个非常古老的话题,但我希望如果他们遇到这个问题,我可以让他们的生活变得更轻松。
您可以非常轻松地使用Mimicc为与 GoogleTest 兼容的 C 函数自动生成模拟。 找到任何声明您要模拟的函数的头文件,将它们“编译”为模拟实现对象文件,并将它们链接到您的测试二进制文件中,包括专门针对 Google 的用户指南中所述的mock_fatal()
和mock_failure()
函数的定义测试。 您必须使用 Mimicc API 与 Mimicc 生成的模拟交互(即它不使用 GoogleMock 的 API 来设置期望等),但它们可以轻松地与 GoogleMock 生成的模拟一起使用。
更具体地说,假设您有一个 C 头文件foo.h
,它声明了一些要模拟的函数。 例如:
/*!
* @param[out] pBuf Destination buffer to read to
* @param[in] sz Size of the read buffer
*/
int magic_read(char *pBuf, const size_t sz);
/*!
* @param[in] pBuf Source buffer to write from
* @param[in] sz Size of the write buffer
*/
int magic_write(const char *pBuf, const size_t sz);
您可以通过使用所有相同的CFLAGS
编译foo.h
来为这些创建模拟,这些CFLAGS
将用于编译随附的生产foo.c
:
prompt$ mimicc -c foo.h -o mock.o --hout=foo-mock.h -DSOME_PREPROC=1 -I <your includes>
要在测试中使用它,请使用foo-mock.h
声明的 API 设置期望值和返回foo-mock.h
,如上面的命令行调用所示。 包括 Google Test 的mock_fatal()
和mock_failure()
实现。
#include <gtest/gtest.h>
#include <memory>
std::unique_ptr<char []> mockErrStr(const char *pLocation, unsigned count, const char *pMsg)
{
const char *pFmtStr = "mock assertion failure! location: '%s',"
" iteration: %d, message: %s";
size_t n = snprintf(NULL, 0, pFmtStr, pLocation, count, pMsg);
std::unique_ptr<char []> outStrBuf(new char[n+1]);
snprintf(outStrBuf.get(), n+1, pFmtStr, pLocation, count, pMsg);
return outStrBuf;
}
void mock_failure(const char *pLocation, unsigned count, const char *pMsg)
{
ADD_FAILURE() << mockErrStr(pLocation, count, pMsg).get();
}
void mock_fatal(const char *pLocation, unsigned count, const char *pMsg)
{
FAIL() << mockErrStr(pLocation, count, pMsg).get();
exit(1);
}
TEST_F(MimiccPoC, test1)
{
char mock_ret_data[] = "HELLO WORLD";
MOCK_FUNCTIONS(foo).magic_read.expect(32);
MOCK_FUNCTIONS(foo).magic_read.andReturn(
1, mock_ret_data, sizeof(mock_ret_data));
char testRetBuf[32];
int testRes = magic_read(testRetBuf, sizeof(testRetBuf));
ASSERT_EQ(1, testRes);
ASSERT_STREQ(testRetBuf, "HELLO WORLD");
}
虽然这看起来很多,但一旦管道设置好,您就可以自动模拟您拥有的任何 C 或 C++ 代码,而无需实际编写或维护其他模拟代码,您只需专注于测试。 从长远来看要容易一些。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.