GoogleTest之Actions的用法

通用示例

namespace mock_action {
    
    
class Foo {
    
    
public:
    virtual ~Foo() {
    
    }
    virtual int& GetBar() = 0;      // 1
    virtual int GetPointerValue() = 0;  // 2
    virtual int CalculateBar() = 0;  // 3
    virtual int Sign(int x) = 0;  // 4
    virtual int DoCombineActions() = 0;  // 5
    virtual void Mutate(bool mutate, int *value) = 0;  // 6
};

class MockFoo : public Foo {
    
    
public:
  MOCK_METHOD(int&, GetBar, (), (override));
  MOCK_METHOD(int, GetPointerValue, (), (override));
  MOCK_METHOD(int, CalculateBar, (), (override));
  MOCK_METHOD(int, Sign, (int x), (override));
  MOCK_METHOD(int, DoCombineActions, (), (override));
  MOCK_METHOD(void, Mutate, (bool mutate, int* value), (override));
}; 
}

返回值

  1. mock函数使用ReturnRef()返回引用
// mock类函数为通用示例中的1

TEST(test_action, mock_action_returnref) {
    
    
  mock_action::MockFoo foo;
  int x = 10;
  EXPECT_CALL(foo, GetBar()).WillOnce(ReturnRef(x));
  EXPECT_EQ(foo.GetBar(), 10);
}

如果将ReturnRef(x)换成Return(x)或者Return(10)都会报语法错误。
2. 返回live value(可以理解为实时值)
Return(x)保存了x的一份拷贝,在后续测试中会一直使用这个拷贝值而不会随着x的改变而改变,在x有改动的情况下就无法满足要求了。要mock的函数的返回值必须是引用才能使用ReturnRef,且不能使用std::ref()(原因TODO)。此时可以通过ReturnPointee解决该问题。

// mock类函数为通用示例中的 2
TEST(test_action, mock_action_returnpointer) {
    
    
    mock_action::MockFoo foo;
    int x = 0;
    EXPECT_CALL(foo, GetPointerValue())
    .WillRepeatedly(ReturnPointee(&x));  // 这样做的意义是什么?
    x = 42;
    EXPECT_EQ(foo.GetPointerValue(), 42);
}

注意,这里action中返回值可以根据动态值而改变。这里mock函数返回值并不是指针,但是action可以使用ReturnPointee,这一点和ReturnRef不同。

Actions的组合

想要执行不止一个action,可以使用::testing::DoAll实现actions的组合:

// mock类函数为通用示例中的 5
TEST(test_action, mock_combine_actions) {
    
    
  mock_action::MockFoo foo;
  EXPECT_CALL(foo, DoCombineActions()).WillOnce(DoAll(
    [](){
    
    
      std::cout << "DoCombineActions 1" << std::endl;
    },
    [] () {
    
    
      std::cout << "DoCombineActions 2" << std::endl;
    },
    Return(10)
  ));
  EXPECT_EQ(foo.DoCombineActions(), 10);
}

输出(EXPECT_CALL改成ON_CALL会报warning?TODO):
在这里插入图片描述

验证复杂参数

TODO

mock副作用

  1. 如果想改变出参,可以使用SetArgPointee()
// mock类函数为通用示例中的 6
TEST(test_action, mock_side_effects01) {
    
    
    mock_action::MockFoo mutator;
    EXPECT_CALL(mutator, Mutate(true, _))
    .WillOnce(SetArgPointee<1>(5));
    int x = 1;
    mutator.Mutate(true, &x);
    EXPECT_EQ(x, 5);
}

SetArgPointee<index>(value) index是从0开始的下标;且value必须支持赋值或者拷贝构造。
如果仍然要返回值,使用DoAll执行这两个action,但要注意需要将Return放到后面
如果出参是数组,则需要使用 SetArrayArgument<N>(first, last)(N只能从0开始吗?TODO)

TEST(test_action, mock_size_effects03) {
    
    
    mock_action::MockArrayMutator mutator;
    int values[5] = {
    
    1, 2, 3, 4, 5};
    int values02[15] = {
    
    1,2,3};
    EXPECT_CALL(mutator, Mutate(NotNull(), 15))
    .WillOnce(SetArrayArgument<0>(values, values+5));  // N只能从0开始?
    mutator.Mutate(values02, 15);
    std::for_each(values02, values02+15, [](int n){
    
    
      std::cout << n << std::endl;
    });
}

改变mock对象的行为

如果想通过某个调用改变mock对象的行为,可以使用 ::testing::InSequence 指定这个调用之前和之后的不同行为
mock类代码

class ChangeBehavior {
    
    
public:
  virtual ~ChangeBehavior() {
    
    }
  virtual bool IsDirty() = 0;
  virtual void Flush() = 0;
  virtual bool FlushIfDirty() = 0;
};

class MockChangeBehavior : public ChangeBehavior {
    
    
public:
  MOCK_METHOD(bool, IsDirty, (), (override));
  MOCK_METHOD(void, Flush, (), (override));
  MOCK_METHOD(bool, FlushIfDirty, (), (override));
};

测试代码

TEST(test_action, mock_change01) {
    
    
  mock_action::MockChangeBehavior my_mock;
  {
    
    
      InSequence seq;
      EXPECT_CALL(my_mock, IsDirty()).WillRepeatedly(
        DoAll(
          [] () {
    
     std::cout << "IsDirty(), return true" << std::endl; },
          Return(true)
          )
      );
      EXPECT_CALL(my_mock, Flush()).WillOnce(
        [] () {
    
     std::cout << "Flush" << std::endl; }
      );
      EXPECT_CALL(my_mock, IsDirty()).WillRepeatedly(
        DoAll(
          [] () {
    
     std::cout << "IsDirty(), return false" << std::endl; },
          Return(false)
          )
      );
  }
  
  EXPECT_EQ(my_mock.IsDirty(), true);
  my_mock.Flush();
  EXPECT_EQ(my_mock.IsDirty(), false);
}

说明:这段代码会在my_mock.Flush()调用之前调用my_mock.IsDirty()时返回true,在my_mock.Flush()调用之后调用my_mock.IsDirty()返回false。

  • 保存结果 TODO

设置返回类型的默认值

  1. 通过::testing::DefaultValue指定默认值。
// mock类函数为通用示例中的 3和4
TEST(test_action, mock_default_value) {
    
    
    int x = 3;
    DefaultValue<int>::Set(x);
    mock_action::MockFoo foo;
    EXPECT_CALL(foo, CalculateBar());  
    EXPECT_CALL(foo, Sign(_));  // 对Sign也是有效的
    EXPECT_EQ(foo.CalculateBar(), 3);
    EXPECT_EQ(foo.Sign(2), 3);
    DefaultValue<int>::Clear();
}

这样设置后对相同类型是全局有效的;要注意使用SetClear的顺序。

  1. 如果要求相同类型默认返回值不同,可以使用ON_CALL+EXPECT_CALL的方式实现这一目的
// mock类函数为通用示例中的 4
TEST(test_action, mock_default_action) {
    
    
    mock_action::MockFoo foo;
    ON_CALL(foo, Sign(_)).WillByDefault(Return(-1));
    ON_CALL(foo, Sign(0)).WillByDefault(Return(0));
    ON_CALL(foo, Sign(Gt(0))).WillByDefault(Return(1));
    EXPECT_CALL(foo, Sign(_)).Times(AnyNumber());
    EXPECT_EQ(foo.Sign(5), 1);
    EXPECT_EQ(foo.Sign(-9), -1);
    EXPECT_EQ(foo.Sign(0), 0);
}

测试程序的输出是没有警告的。
Note that both ON_CALL and EXPECT_CALL have the same “later statements take precedence” rule, but they don’t interact. That is, EXPECT_CALLs have their own precedence order distinct from the ON_CALL precedence order.

使用自定义函数作为Actions

class ACFoo {
    
    
public:
  virtual ~ACFoo(){
    
    };
  virtual int Sum(int x, int y) = 0;
  virtual bool ComplexJob(int x) = 0;
};

class ACMockFoo : public ACFoo {
    
    
public:
  MOCK_METHOD(int, Sum, (int x, int y), (override));
  MOCK_METHOD(bool, ComplexJob, (int x), (override));
};

int CalculateSum(int x, int y) {
    
    
  std::cout << "Enter in CalculateSum, x: " << x << ", y: " << y << std::endl;
  return x + y;
}

int Sum3(int x, int y, int z) {
    
    
  std::cout << "Sum3" << std::endl;
  return x + y + z;
}

int NewPermanentCallback(const std::function<int(int,int,int)>& f, int x) {
    
    
  std::cout << "Enter in NewPermanentCallback" << std::endl;
  return f(1,2,3);
}

class Helper {
    
    
public:
  bool ComplexJob(int x) {
    
    
    std::cout << "Helper::ComplexJob" << std::endl;
    return true;
  }
};

验证代码:

TEST(test_action, mock_use_functor01) {
    
    
  mock_action::ACMockFoo foo;
  mock_action::Helper helper;
  EXPECT_CALL(foo, Sum(_, _))
    .WillOnce(&mock_action::CalculateSum)  // 1
    .WillRepeatedly(Invoke(mock_action::NewPermanentCallback(mock_action::Sum3, 1)));  // 2 这里不正确,编译报错TODO
  EXPECT_CALL(foo, ComplexJob(_))
  .WillOnce(Invoke(&helper, &mock_action::Helper::ComplexJob))  // 3
  .WillOnce([] {
    
     std::cout << "lambda 1 " << std::endl; return true;})  // 4
  .WillRepeatedly([](int x) {
    
     std::cout << "lambda 2 " << std::endl; return x > 0;});  // 5

  foo.Sum(5,6);
  foo.ComplexJob(10);
  foo.ComplexJob(-1);
  foo.ComplexJob(3);
}

分析:

  1. 使用全局自由函数,注意函数签名要一致
  2. 这里有问题,编译出错,需要解决
  3. 使用其他对象的成员函数,函数签名也要保持一致
  4. 4和5都是用lambda表达式,但是4的签名和mock函数不一样,为什么没有报错?TODO

参数绑定

如果作为action的这些函数参数个数多于mock函数,可以通过预先绑定的方式(类似std::bind)

using ::testing::Invoke;

class MockFoo : public Foo {
    
    
 public:
  MOCK_METHOD(char, DoThis, (int n), (override));
};

char SignOfSum(int x, int y) {
    
    
  const int sum = x + y;
  return (sum > 0) ? '+' : (sum < 0) ? '-' : '0';
}

TEST_F(FooTest, Test) {
    
    
  MockFoo foo;

  EXPECT_CALL(foo, DoThis(2))
      .WillOnce(Invoke(NewPermanentCallback(SignOfSum, 5)));
  EXPECT_EQ('+', foo.DoThis(2));  // Invokes SignOfSum(5, 2).
}
  • 但是这里的NewPermanentCallback是什么?TODO

无参调用

bool Job1() {
    
     ... }
bool Job2(int n, char c) {
    
     ... }

...
  MockFoo foo;
  EXPECT_CALL(foo, ComplexJob(_))
      .WillOnce([] {
    
     Job1(); });
      .WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a')));

  foo.ComplexJob(10);  // Invokes Job1().
  foo.ComplexJob(20);  // Invokes Job2(5, 'a').

使用InvokeWithoutArgs替代Invoke,这里相当于将两个参数都绑定了,所以在调用时无需指定参数

使用函数指针或用作参数的functor作为action

使用InvokeArgument<N>(arg_1, arg_2, ..., arg_m)会调用mock函数接收到的第N个参数,这个参数可以是函数指针,functor,或者回调。

MOCK_METHOD(bool, DoThis, (int n, (ResultCallback1<bool, int>* callback)),(override));
...
EXPECT_CALL(foo, DoThis(_, _)).WillOnce(InvokeArgument<1>(5));

如果这个参数(函数指针或functor)带有引用参数,可以使用std::ref()

...
  MOCK_METHOD(bool, Bar,
              ((ResultCallback2<bool, int, const Helper&>* callback)),
              (override));
  EXPECT_CALL(foo, Bar(_))
      .WillOnce(InvokeArgument<0>(5, std::ref(helper)));            
  • 未验证 TODO

忽略Actions的结果

class ACFoo {
    
    
public:
  virtual ~ACFoo(){
    
    };
  virtual int Sum(int x, int y) = 0;
  virtual bool ComplexJob(int x) = 0;
  virtual void Sum1(int x, int y) = 0;
  virtual bool Sum2() = 0;
};

class ACMockFoo : public ACFoo {
    
    
public:
  MOCK_METHOD(int, Sum, (int x, int y), (override));
  MOCK_METHOD(bool, ComplexJob, (int x), (override));
  MOCK_METHOD(void, Sum1, (int x, int y), (override));
  MOCK_METHOD(bool, Sum2, (), (override));
};
string DoSomething() {
    
    
  std::cout << "DoSomething" << std::endl;
  return "a";
}
int CalculateSum(int x, int y) {
    
    
  std::cout << "Enter in CalculateSum, x: " << x << ", y: " << y << std::endl;
  return x + y;
}
TEST(test_action, mock_ingnore_result) {
    
    
   mock_action::ACMockFoo foo;
   EXPECT_CALL(foo, Sum1(_, _)).WillOnce(IgnoreResult(mock_action::CalculateSum));
   foo.Sum1(1,3);
   EXPECT_CALL(foo, Sum2()).WillOnce(DoAll(IgnoreResult(mock_action::DoSomething), Return(true)));
   EXPECT_EQ(foo.Sum2(), true);
}
  • 可以用IgnoreResult()忽略返回值,但这两种写法都编译不过

可选的Actions参数

加入一个mock函数有7个参数,但action函数只有三个,一个比较笨拙的办法是定义一个7个参数的函数并在函数内调用这个三参数函数。
gmokc提供了更简便的方法:WithArgs<N1, N2, ..., Nk>(action),N1到 Nk是action对应于mock函数的参数位置,如:

EXPECT_CALL(mock, Foo)
      .WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1))); 

汇总如下:

Action Description
WithArg<N>(a) Pass the N-th (0-based) argument of the mock function to action a and perform it.
WithArgs<N1, N2, ..., Nk>(a) Pass the selected (0-based) arguments of the mock function to action a and perform it.
WithoutArgs(a) Perform action a without any arguments.

Actions汇总

猜你喜欢

转载自blog.csdn.net/u010378559/article/details/131175578