GTest框架FFF备忘录

# 宏备忘录

描述 例子
FAKE_VOID_FUNC(fn [,arg_types*]); 定义一个模拟函数,函数返回值类型为void,函数名为fn,函数形参为arg_types(可选项)。 FAKE_VOID_FUNC(DISPLAY_init); FAKE_VOID_FUNC(DISPLAY_output_message, const char*);
FAKE_VALUE_FUNC(return_type, fn [,arg_types*]); 定义一个模拟函数,函数返回值类型为return_type,函数名为fn,函数形参为arg_types(可选项)。 FAKE_VALUE_FUNC(int, DISPLAY_get_line_insert_index);
FAKE_VOID_FUNC_VARARG(fn [,arg_types*], …); 定义一个带有可变参数的模拟函数,函数返回值类型为void,函数名为fn,函数形参为arg_types(可选项)。 FAKE_VOID_FUNC_VARARG(fn, const char*, …)
FAKE_VALUE_FUNC_VARARG(return_type, fn [,arg_types*], …); 定义一个带有可变参数的模拟函数,函数返回值类型为return_type,函数名为fn,函数形参为arg_types(可选项)。 FAKE_VALUE_FUNC_VARARG(int, fprintf, FILE*, const char*, …)
RESET_FAKE(fn); 重置模拟函数fn的状态信息。 RESET_FAKE(DISPLAY_init);

# 模拟函数内部逻辑

FAKE_VALUE_FUNC(int, intfunc, char, char);

void test(void)
{
    intfunc_fake.custom_fake = [](char, char) -> int 
    {
        return 1;
    }
    int ret = intfunc('a','b');
}

或者
    
FAKE_VALUE_FUNC(int, intfunc, char, char);

int fake_intfunc(char a, char b)
{
    return 1;
}

void test(void)
{
    intfunc_fake.custom_fake = fake_intfunc; 
    int ret = intfunc('a','b');
}

# 验证函数的参数返回值记录

框架内部会记录每个模拟函数的最后十次被调用的参数值和返回值,每个伪函数的每个参数值和返回值都会记录

FAKE_VALUE_FUNC(int, intfunc, char, char);

void test(void)
{
     intfunc_fake.custom_fake = [](char, char) -> int 
    {
        return 1;
    }
    int ret = intfunc('a', 'b');
    ASSERT_EQ('a', intfunc_fake.arg0_val);		
    ASSERT_EQ('b', intfunc_fake.arg1_val);		
    ASSERT_EQ(ret, intfunc_fake.return_val);	
  
}

# 模拟函数返回值序列

在单元测试中,有时候会多次调用同一个外部依赖函数,并且期望每次调用都返回不同值。FFF框架实现此操作的方法是,为模拟函数指定返回值序列。例如:

FAKE_VALUE_FUNC(long, longfunc);

void test(void)
{
    long myReturnVals[3] = { 3, 7, 9 };
    SET_RETURN_SEQ(longfunc, myReturnVals, 3);
    ASSERT_EQ(myReturnVals[0], longfunc());
    ASSERT_EQ(myReturnVals[1], longfunc());
    ASSERT_EQ(myReturnVals[2], longfunc());
    ASSERT_EQ(myReturnVals[2], longfunc());
    ASSERT_EQ(myReturnVals[2], longfunc());
}

通过使用SET_RETURN_SEQ宏指定返回值序列,模拟函数将按顺序返回数组中给出的值。当到达序列的末尾时,模拟函数将继续无限期地返回序列中的最后一个值。

# 模拟函数返回函数指针序列

接上节,若我们想实现每次调用fake函数的时候,每次走不同的函数逻辑,该怎么做呢?参考如下:

FAKC_VOID_FUNC(voidfunc1outparam,char *);
void voidfunc1outparam_custom_fake1(char *a)
{
    *a = 'x';
}

void voidfunc1outparam_custom_fake2(char *a)
{
    *a = 'y';
}

void voidfunc1outparam_custom_fake3(char *a)
{
    *a = 'z';
}

TEST_F(FFFTestSuite, custom_fake_sequence_not_exausthed)
{
    void (*custom_fakes[])(char *) = {voidfunc1outparam_custom_fake1,
                                      voidfunc1outparam_custom_fake2,
                                      voidfunc1outparam_custom_fake3};
    char a = 'a';

    SET_CUSTOM_FAKE_SEQ(voidfunc1outparam, custom_fakes, 3);

    voidfunc1outparam(&a);
    ASSERT_EQ('x', a);
    voidfunc1outparam(&a);
    ASSERT_EQ('y', a);
    voidfunc1outparam(&a);
    ASSERT_EQ('z', a);
}

外部3次调用中,每次都进行不同的函数逻辑,a依次赋值为x,y,z

# 参数化测试

FAKE_VALUE_FUNC(int, intfunc, char);
const std::tuple<int, char> TUPLE[]{ { 1, 'a' },{ 2, 'b' },{ 3, 'c' } };

//使用TEST_P,测试类必须继承::testing::WithParamInterface<T>  T就是你需要参数化的参数类型,这里使用std::tuple保存参数
class TestName : public TestCli, public ::testing::WithParamInterface<tuple<int, char> > {
    //...........
    //...........
    //...........
}
TEST_P(TestName, test1)
{
    // arrange
    // get<0>  => tuple.get<0>拿到元组的第一个元素    GetParam()获取参数的值
	intfunc_fake.custom_fake = [](char) -> int 
    {
        return 1;
    }

    // act
	int ret = intfunc(get<1>(GetParam()))
   

    // assert
    ASSERT_EQ(ret, get<0>(GetParam()));
}
//实现参数化测试用例  ValuesIn(STL container) or ValuesIn(iter1,iter2)
INSTANTIATE_TEST_CASE_P(TestCaseName, TestName,
    ::testing::ValuesIn(TUPLE));

# EXPECT_CALL和ON_CALL

到目前为止,我注意到有两种方法来打桩函数

ON_CALL(mock, methodX(_)).WillByDefault(do_action);

要么

EXPECT_CALL(mock, methodX(_)).WillOnce(do_action));

当你使用EXPECT_CALL打桩methodX函数时,若methodX在测试时不被调用,将会报错never called – unsatisfied and active

FooMain.cc:35: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)))…
Expected: to be called once
Actual: never called – unsatisfied and active

如果你不能确定,或因为某些原因(每个测试用例的参数不对齐)可以使用ON_CALL代替EXPCET_CALL,ON_CALL意指当打桩的methodX函数被调用时,执行do_action,若测试时methodX不被调用,只会发出警告,因为ON_CALL没有期望,只会回应CALL并发出动作

NOTE: You can safely ignore the above warning unless this call should not happen.  Do not suppress it by blindly adding an EXPECT_CALL() if you don't mean to enforce the call.  
See https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#knowing-when-to-expect for details.

备注: [更多内容请参考官方文档]: https://github.com/meekrosoft/fff