Google Test 测试架构探究

上一篇 / 下一篇  2012-08-09 10:01:18 / 个人分类:杂谈

-fB/q)s2Mc0  得益于和萱哥关于单元测试的聊天,让我开始想要了解Google的单元测试框架GoogleTest,(虽然以前也听勇哥提到过这个词,但是一直没有往心里去);以前基于C#反射特性Demo过一个C#的单元测试框架(http://www.51testing.com/?action-viewnews-itemid-818965),所以更想明白Google Test测试框架的实现机制;这期间搞过一段Python,看Python文档时也看到相关的单元测试框架PyUnit,但没有深入研究,有时间深入研究一下。

9`Z Wh OG0

d1MP+p \@)j0  自动化测试框架,主要目的就是自动化调用执行某些测试用例,将执行结果与目标结果进行比较,用以达到测试特定目标的目的。而测试用例的针对目标可能是函数接口,功能模块,UI等等。51Testing软件测试网&j\2K-sxRf

5K x)@WW6e0   自动化测试框架从功能上可以分为技术框架和执行框架。所谓技术框架一般针对于特定的测试目标,为了达到自动化测试而引入的技术或者方法,比如微软UI测 试中的MSAA,UI Automation框架以及后续建立在UIAutomation上的POM/LFM(详见References中微软UI自动化测试技术演进相关链 接)。而执行框架则侧重于对测试用例自动化执行的控制。这篇文章主要侧重于讨论执行框架,而自动化执行框架的设计中有以下几方面是必须考虑的:51Testing软件测试网2G}!J.EG8DK

m8}F/fj B.Rxl#r$S]'O0  1、测试用例选择控制。

yA1uKW(v_0

e QOq*~(UP&^-H a0  2、自动化传递参数或者配置条件。51Testing软件测试网 A.V}O0V5k j-m:U

51Testing软件测试网p)?2s(U,|9O+Q(L/F*j

  3、测试用例执行结果比对。

K"tV\{ hM)W051Testing软件测试网0H BfC5yg,g'p n

  4、测试结果记录与分析。51Testing软件测试网 c7f v#hB3m

51Testing软件测试网T)g.|(g+Z/OJ

  测试用例选择控制

-Y;@3qdTD0

k$O L}SO Z OZ0  测试用例由多人编写,且针对模块不同,再加上历史原因,诸如此类限制情况要选择特定测试用例执行。C#中常见做法是利用反射机制建立特性标签,选择带有特定标签的测试用例执行,而C++常见做法是给测试用例加字符串参数,执行时通过字符串参数进行选择。51Testing软件测试网rWZ3I]X

51Testing软件测试网 N@~V:Wq^f'tS

  自动化传递参数或者配置条件51Testing软件测试网Z5W!v{8WMf

H)Si#@gOrv%t0   给被测试测试目标传递参数或配置文件是自动化测试框架必须有的特性,最可取的做法就是利用XML文档动态传参,好处是实现代码复用(针对同一个测试方法 传递不同测试参数),以及可以动态自适应测试参数的变化(代码不改动的情况下调整参数或者配置信息适应测试条件的改变)。51Testing软件测试网q J*s6l;A,_,}"wRb

"Ou2HZUzz0  测试用例执行结果比对51Testing软件测试网,@ p$C%N~ Qy

51Testing软件测试网IY~tv~p0@:A,z

  测试比对一般代码中实现测试结果比对,但这涉及到目标结果也就是期望结果的取得的问题,其实和上述传参和配置条件属于同一个范畴。

MP\+T,g os4m051Testing软件测试网x^r+T5|&C

  测试结果记录与分析51Testing软件测试网:\ f5r M.g

51Testing软件测试网'O2]lR5c

  结果记录一般会将结果可视化输出到CMD窗口或者桌面窗口,XML文件,HTML文件,Excel文件等。不同测试框架实现有所不同,取决于需求。51Testing软件测试网 L ?eZ:L Z

51Testing软件测试网1P#O#u6d U(~3\

  Google Test51Testing软件测试网:duV%qr}1{

51Testing软件测试网#{#UEac8e*}*h

  本来想Demo以下Google Test的实现,但是发现网上已经有人做过类似的事情,就把代码copy过来了,尊重作者意愿,将连接放至refereces。

zj4U*?:f051Testing软件测试网4me| R'D4cS

   Goolge Test架构主题设计很简单,将每一个测试用例封装到一个类TestCase的子类里。一个测试单元类UnitTest中将这些TestCase存入一个 Vector的数据结构中,使用RunTestCases()方法(原著中使用Run这个方法名起的不是很好容易与TestCase中Run()方法相 混,降低代码可读性)控制取出Vector中的测试用例来控制执行过程,结果比对等。51Testing软件测试网&VJq V ]]#AF

51Testing软件测试网+`(@4J0@T@ S y9s~[

  这个架构最出彩的地方是使用宏定义掩盖了繁杂的测试用例封装过程,可谓是神来之笔,详尽代码请参考连接,于作者博客下载。

?#Z!JcOJ%P.K U0

CS\!ji@h0class TestCase
T!o2H}6T4y6s0{
3P&Z|)k%iF1H8]0public:51Testing软件测试网|%S I gyn3v
    TestCase(const char* case_name) : testcase_name(case_name){}

)WG!y'J2Y5_Uk0

/FCYXc0`0    // 执行测试案例的方法
4i(GZlO-q[)h%m.AD)b`0    virtual void Run() = 0;

"EE,b1S Kj.b*V7` ^051Testing软件测试网 S!tnimc*u Ih

    int nTestResult; // 测试案例的执行结果
zVDq"y2p B0    const char* testcase_name; // 测试案例名称
LK;Lp"B5S*FC0};

W j*\ Jb051Testing软件测试网/yL(wn z.~C

class UnitTest
'@;F;W)Kukc0{
)E/]*WF*Pv2o&[0public:51Testing软件测试网%S Pr d$Gg^;gQ;R
    // 获取单例51Testing软件测试网2v*i6P%f1]tQG
    static UnitTest* GetInstance();
51Testing软件测试网1u%u.XQ/W,nI

0j#A E,y,o?g0    // 注册测试案例
mL;f)d$NcQ0    TestCase* RegisterTestCase(TestCase* testcase);
9P7C'i"OL%[aG0   51Testing软件测试网^&Xhju,a s
    // 执行单元测试
S*]"W$S:{OwBG0    int RunTestCases();

!qH.n+I-MN0

A'T{!E#sa0    TestCase* CurrentTestCase; // 记录当前执行的测试案例51Testing软件测试网Zo%{/jM-hthZ'NE
    int nTestResult; // 总的执行结果51Testing软件测试网 \ pP0D;y;Pf#n5[
    int nPassed; // 通过案例数
/g"q*|"LD&Z.s@5q$I0    int nFailed; // 失败案例数51Testing软件测试网0_-Sj m-DVHdV jN+u
protected:51Testing软件测试网7Hm&g8Z.@;l*@
    std::vector<TestCase*> testcases_; // 案例集合51Testing软件测试网k1h,@a p/QA)w P
};

9jzo`%y8u8S-p#|5UE0

_!kD:xUM0// 以下这段宏定义掩盖了繁杂的测试用例封装过程51Testing软件测试网;E0Hxf$U:M5IG7s
#define TESTCASE_NAME(testcase_name) \51Testing软件测试网X~&vleq7ytuU
    testcase_name##_TEST      //##的作用在于(token-pasting)符号连接操作符,即将宏定义的多个形参成一个实际参数名,在这里testcase_name_TEST

3w{ `&Mhje0

Oi.{QK%Ry(T0#define NANCY_TEST_(testcase_name) \51Testing软件测试网{ N8r7J9MN M
class TESTCASE_NAME(testcase_name) : public TestCase \51Testing软件测试网^+[ChL7^6g
{ \
;e&d9Q._m:}9y(`0public: \51Testing软件测试网 R+O8Y(h'^
    TESTCASE_NAME(testcase_name)(constchar* case_name) : TestCase(case_name){}; \51Testing软件测试网9N t#CaT!s
    virtualvoid Run(); \51Testing软件测试网NWa j,Y1t(BX8TEXR
private: \51Testing软件测试网'F}Y1GX_\4e
    static TestCase*const testcase_; \51Testing软件测试网z"s5p2vF.vzY:p5x2S
}; \
;BLR4P'^!{en0\51Testing软件测试网iy io"O2Z
TestCase*const TESTCASE_NAME(testcase_name) \
x-r7m4K[?0    ::testcase_ = UnitTest::GetInstance()->RegisterTestCase( \
q]@V%h^SU0        new TESTCASE_NAME(testcase_name)(#testcase_name)); \ 51Testing软件测试网y&r9Hx:j.F~
 //#号的作用是(stringizing)字符串化操作符。其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。("testcase_name").
`a(S%K1fs l6I0void TESTCASE_NAME(testcase_name)::Run() 
Z3p*K X/p2C/V0/*注意Run()后边没有{},之所以这么做是宏定义将测试用例放入到Run的方法主体里。例如51Testing软件测试网zK sc@ ` wR
NTEST(FooTest_PassDemo)51Testing软件测试网A BgZ2M
{51Testing软件测试网 u~Rw'Uc h
    EXPECT_EQ(3, Foo(1, 2));
_ _)qF8K#z3_&~o1c6z0    EXPECT_EQ(2, Foo(1, 1));51Testing软件测试网.k a1hjN?3Ny
}51Testing软件测试网8V~o~\sM
上述代码中EXPECT_EQ(2, Foo(1, 1));代码放入到Run的方法主体里。51Testing软件测试网%i0IjE kTz,v
*/                 
51Testing软件测试网8b*C*\H2H*vw9G7h[ K

8^lRn Rln2S0
W@0L*hC%J0n0#define NTEST(testcase_name) \51Testing软件测试网/Woy0vh,S:_
    NANCY_TEST_(testcase_name)
DQ)H-b#Y z&CBI1b)W0#define RUN_ALL_TESTS() \
4k6j4O0}!CE0 UnitTest::GetInstance()->RunCases();

(W'Ym;RiC!V:v }051Testing软件测试网s#@{7],y


3c#T.mf;a0#define EXPECT_EQ(m, n) \51Testing软件测试网 MFS8T t\
    if (m != n) \
Jz}O ?R%sd0    { \51Testing软件测试网!fR8W,x2u2iIV'hk@t
        UnitTest::GetInstance()->CurrentTestCase->nTestResult = 0; \51Testing软件测试网2erx^q2Q;SB}
        std::cout << red << "Failed" << std::endl; \51Testing软件测试网1U @*v `#xk
        std::cout << red << "Expect:" << m << std::endl; \51Testing软件测试网Y WC O-X7?
        std::cout << red << "Actual:" << n << std::endl; \51Testing软件测试网.Q^[6@&lZX,t
    }

'V/b#qD2c051Testing软件测试网7N"lF3Y'J;z

  例如以下测试Foo方法,NTEST(FooTest_PassDemo)就是创建一个名为FooTest_PassDemo_Test的子类,将宏定义的断言EXPECT_EQ()等放入Run()方法主题中。展开代码如下。51Testing软件测试网#^0{ jhKH D q

51Testing软件测试网f0h2?3YYM

51Testing软件测试网S*Fu.PE

/G+M9y2q"ooF*w0int Foo(int a, int b)51Testing软件测试网 `0l^-A^ C
{51Testing软件测试网{(]$q?M
    return a + b;
:jY!F{3k7xu+M0}

`E3_fE#d~l0D/~4t0

'MU/Lg,Xr4O0NTEST(FooTest_PassDemo)51Testing软件测试网RP)@U*DH+c i
{
-t#wF'XJ\[ P0    EXPECT_EQ(3, Foo(1, 2));
@q|0b J)W0    EXPECT_EQ(2, Foo(1, 1));51Testing软件测试网0w%a`6B[+Y/P
}

tQ3ow8Q051Testing软件测试网 x9a3[,UF Q

// 将以上宏定义展开等价于以下代码。

6y\3i H'Kj*v051Testing软件测试网Rx_@ bQ!Z9D*L \

class FooTest_PassDemo_TEST : public TestCase
z`|Sd)G0{
gJ.K d(NG%H:X5d_0public:51Testing软件测试网$|,P$LXdJ
    FooTest_PassDemo_TEST(const char* case_name) : TestCase(case_name){};
5OUYe&H"lT0    virtual void Run();
| j ??\0?1Of0private:51Testing软件测试网8g A+?:B%p;[,}C
    static TestCase* const testcase_;
T/X9|jX&q5yH0};

~/Um:yZ!O0

(a$kZ_ Ky0TestCase* const FooTest_PassDemo_TEST::testcase_ = UnitTest::GetInstance()->RegisterTestCase(new FooTest_PassDemo_TEST("FooTest_PassDemo"));51Testing软件测试网.U^ek![ SMg[
void FooTest_PassDemo_TEST::Run()
|uM0t-\,_ L,],]WA0{51Testing软件测试网)c#U/O;|S6b6JG }.~
    if (3 != Foo(1, 2))
D/G&P7Y"CSlv%s0    {51Testing软件测试网+E6TR8e;@ j@!p&mM
    UnitTest::GetInstance()->CurrentTestCase->nTestResult = 0;51Testing软件测试网tCV1HM-L0?
    std::cout << red << "Failed" << std::endl;51Testing软件测试网2_-]N"v+d)fg+O*i3X
    std::cout << red << "Expect:" << Foo(1, 2) << std::endl;51Testing软件测试网Hk+YR0T1~ l
    std::cout << red << "Actual:" << 3 << std::endl;
\.L2e6p0?-Yl0    }
P5hI@ u0    if (2 != Foo(1, 1))51Testing软件测试网#~Ja}+H"Z&o h w
    {
,s-v||Xp6[0    UnitTest::GetInstance()->CurrentTestCase->nTestResult = 0;51Testing软件测试网rwfR.f+x,`
    std::cout << red << "Failed" << std::endl;51Testing软件测试网r"W-d*ujf
    std::cout << red << "Expect:" << Foo(1, 1) << std::endl;51Testing软件测试网k:x/c mr
    std::cout << red << "Actual:" << 2 << std::endl;
I6hO#^dHQJu0    }
7mYve0w"|-{1Lm/B0}
51Testing软件测试网y4y+f:le0EjJqS

51Testing软件测试网*l+W"H2K mv[

int _tmain(int argc, _TCHAR* argv[])51Testing软件测试网:TP)? _ m~x
{51Testing软件测试网vbif,c3Wfp
 51Testing软件测试网/dq j v^7`$~3}
 //return UnitTest::GetInstance()->Run();51Testing软件测试网 vb(XxB v
 return RUN_ALL_TESTS();51Testing软件测试网j%^C8S"p
}

:eSQ9fS#C0

%d(e5bqe0  坐在班车上,脑子里回想着这篇博客,突然间有种模糊的意识,COM的设计思想貌似也可以用来设计单元测试框架,没有成熟的思路,只是个想法,写下以备忘。51Testing软件测试网 f U0ojN$i5|


TAG:

 

评分:0

我来说两句

Open Toolbar