C++单元测试--Google Mock入门

概述

白盒测试:全面了解程序内部逻辑结构,对所有逻辑路径进行测试。

单元测试:对软件基本组成单元进行的测试,这里的单元是软件设计的最小单位,单元测试属于白盒测试范畴。

打桩:在做单元测试或者集成测试时,如果某个程序单元的某条语句需要调用的一个外部函数还没有完成的话,可以简单让它返回几个支持测试用例的值,这种状态的外部函数就是“打桩”

Google Mock是google推出的一个开源的白盒测试工具,可以配合Google Test来做C++的单元测试打桩。一个项目可能需要多个人来协作完成,在进行单元测试的时候,你可能并不关心你所调用的接口是如何实现的,只需要测试你的代码逻辑、接口是否调用以及传入的参数是否正确,这种情况下使用Google Mock进行单元测试非常合适。

使用流程

一、创建Mock类

比如将Mock函数定义(MOCK_METHOD*)放到Mock类的public:部分中,这样做是为了让ON_CALL和EXPECT_CALL可以从Mock类外引用Mock函数。

MOCK_METHOD#1(#2,#3(#4))

#1:要mock的方法共有几个参数

#2: 要mock的方法名称

#3:表示方法的返回值类型

#4:方法的具体参数

例如: 由Google Mock来Mock一个自由函数,Mock并不直接调用自由函数,而是为它引入一个接口,并需要针对这个接口实现对自由函数的调用

class Foo { 
 public: 
 ... 
   virtual bool Transform(Gadget* g) = 0; 
 protected: 
   virtual void Resume(); 
 private: 
   virtual int GetTimeOut(); 
}; 

class MockFoo : public Foo { 
 public: 
    ... 
    MOCK_METHOD1(Transform, bool(Gadget* g)); 
 // The following must be in the public 
 //section, even though the methods are protected or private in the base class.
    MOCK_METHOD0(Resume, void()); 
    MOCK_METHOD0(GetTimeOut, int()); 
};

如果一个没有指定EXPECT_CALL的mock函数被调用了,Google Mock会打印一个“uninteresting call”警告.

二、Using Matchers

可以精确指定一个Mock函数期望的参数是什么

1. 设置单个参数,例如:

using ::testing::Return; 
... 
EXPECT_CALL(foo, DoThis(5)) 
    .WillOnce(Return('a')); 
EXPECT_CALL(foo, DoThat("Hello", bar));

2. 有时候单独匹配参数是不够的,可以匹配函数的所有参数

例如:with子句,在with内的语句比如是是一个Match<tr1::tuple<A1, ..., An> >类型的Matcher,其中A1,..., An是函数参数的类型。

以下代码目的:InRange第一个参数必须非0,并且必须小于第二个参数

using ::testing::_; 
using ::testing::Lt; 
using ::testing::Ne; 
... 
EXPECT_CALL(foo, InRange(Ne(0), _)) 
    .With(Lt());

三、 Setting Expectations

1. 基本使用

EXPECT_CALL(mock_object, method(matcher1, matcher2, ...))
    .With(multi_argument_matcher) //多个参数的匹配方式指定
    .Times(cardinality) //执行多少次
    .InSequence(sequences) //函数执行顺序
    .After(expectations) //指定某个方法只能在另一个方法后执行
    .WillOnce(action) //一般使用return,指定一次调用的输出
    .WillRepeatedly(action) //一直输出
    .RetiresOnSaturation(); //期待调用不会被相同的函数期待所覆盖

例如: 如果一个Mock函数不应该被调用

using ::testing::_; 
... 
EXPECT_CALL(foo, Bar(_)) 
    .Times(0);

2. 设置函数执行顺序

如果希望函数以期望的顺序执行,则需要将EXPECT_CALL()语句放到一个InSequence对象的生命周期里
例如以下例子,调用顺序:先调用foo.DoThis(5),然后两次参数为任意的bar.DoThat()调用,最后调用一次foo.DoThis()。如果调用的顺序与上面不符,则Google Mock会报告一个错误。

using ::testing::_; 
using ::testing::InSequence; 
{ 
    InSequence s; 
    EXPECT_CALL(foo, DoThis(5)); 
    EXPECT_CALL(bar, DoThat(_)) 
        .Times(2); 
    EXPECT_CALL(foo, DoThis(6)); 
}

 以下代码的执行顺序:S1是A->B,S2是A->C->D

using ::testing::Sequence; 
Sequence s1, s2; 
EXPECT_CALL(foo, A())
    .InSequence(s1, s2); 
EXPECT_CALL(bar, B()) 
    .InSequence(s1); 
EXPECT_CALL(bar, C()) 
    .InSequence(s2); 
EXPECT_CALL(foo, D()) 
    .InSequence(s2);

四、Using Actions

Return(x)在创建时就会保存一个x的拷贝,在它执行的时候总是会返回相同的值。

ReturnPointee(pointer) 在动作执行的时候返回指针指向的值

返回引用ReturnRef()

using testing::ReturnPointee; 
... 
int x = 0; 
MockFoo foo; 
EXPECT_CALL(foo, GetValue()) 
    .WillRepeatedly(ReturnPointee(&x)); // Note the & here. 
x = 42; 
EXPECT_EQ(42, foo.GetValue()); // This will succeed now.

猜你喜欢

转载自blog.csdn.net/weixin_42670653/article/details/81741814