用pytest做服务端自动化协议测试


现在在做的一个游戏项目开发时需要使用unity客户端进行调试,但身为一个服务端程序员,尝尝需要先行开发以便客户端调试,之前的做法是开发完成后,写好对应的GM指令,不够便捷。所以萌生一个想法,写一个测试客户端,这样不需要更新unity客户端,等待漫长的编译过程,在工作的后期又看到了pytest这个库,尝试结合起来做一个自动化协议测试的东西。


http://blog.csdn.net/q_yang1987/article/details/52194860之前写的这篇文章描述的就是这个项目的私有协议的python序列化反序列化模块实现,期间对FixSizedString类型字段做了一些优化,主要方便直接赋值字符串而不用使用类似set_str这样的函数。


先介绍一下pytest这个东西,官网地址http://docs.pytest.org/en/latest/contents.html,pytest功能丰富:

1. hack了assert,如果断言失败可以进行debug(pdb),如果测试用例有输出也会打印出来,反之,如果测试通过,则没有任何输出。

2. fixture,定义测试用例用到的参数(或者准确说是测试所需要的基础设施),可以对这些基础设置设置初始化步骤以及析构步骤,对于协议测试,一个client一般都需要先进行登录,然后测试,最后登出,这些重复的代码只需要在fixture中写一遍就好,同时fixture也可以指定scale(测试用例级,模块级,session级)。

3. 丰富的插件,没用到......

4. 一些好用的decorator,比如需要坚固服务器版本时,可以定义某些测试用例必须要求服务端版本达到多少,否则略过

5. 还有许多判定方式,比如期待异常,期待失败等等


首先网游的协议测试需要一个客户端,最好是异步的,并且在需要读取回包的时候方便读取,不需要读取回包的时候可以尽情忽略。由于pytest可以在命令行启动,这也是自动化测试最理想的方式,然而这种方式没有一个统一明确的入口,如何将异步client结合进pytest的主循环很关键。我的做法是定义fixture的时候做异步init操作,并加一个标记,init需要做的事情是新开启一个线程启动异步IO的循环,为什么需要一个独立的线程?这里采用的是asyncore做异步IO,其实正常使用时不需要一个独立线程跑asyncore loop的,只需要主线程在loop的时候传入timeout,然后做逻辑,再loop,并一直循环下去就可以了。这种方式不适合与pytest结合,毕竟主线程在跑pytest,我们整合不进去。


这带来的一个问题是:最后怎么结束asyncore 的loop,其实fixture可以定义一个session级别的fixture,这个fixture可以充当最终测试结束的信号,并进行相应操作:

@pytest.fixture(scope="session")

def fini():

global STOP

yield "bye bye"

STOP = True

使用yield分隔fixture的初始化和析构步骤是推荐的,然而在实际使用的时候貌似有bug.....


用一个测试用例来演示一下过程:

首先是定义client,用来进行交互(这一步只需要一遍):

@pytest.fixture(scope="module")
def clientA(request, fini):
    cli = None
    init()
    def fin():
        if cli:
            cli.handle_close()
        time.sleep(0.3)
    request.addfinalizer(fin)
    cli = baseclient.B2Client(*(MY_SERVER + XiaoMing))
    cli.login()
    return cli

@pytest.fixture(scope="module")
def clientB(request, fini):
    cli = None
    init()
    def fin():
        if cli:
            cli.handle_close()
        time.sleep(0.3)
    request.addfinalizer(fin)
    cli = baseclient.B2Client(*(MY_SERVER + DaMing))
    cli.login()
    return cli

这两个client都是module级别的,也就是一个py文件会新生成一个(上一个会销毁),同时在返回之前,调用了login,这些都是每个测试用例的前置重复操作,可以提出到fixture中,这里没有用yield分隔初始化以及析构步骤,因为发现module级别结合yield有bug,不明真相。


然后写协议,协议使用的就是之前介绍的私有化协议,不再赘述(http://blog.csdn.net/q_yang1987/article/details/52194860)


最后写测试用例:

比如在进行的一个多人玩法,创建房间/加入/销毁房间:

@define.minversion(3, 4)

def test_new_room(clientA, clientB, capsys):

#new a private room

new_room = proto.MatchNewRoom()

new_room.uid = 100000010

new_room.map_type = 1

new_room.join = 0

new.b_private = 1

clientA.send_message(new_room)


resp = clientA.read(proto.MatchNewRoomResp.CMD)

assert resp

assert resp.error_code == 0


#join room

join_room = proto.MatchJoinRoom()

join_room.uid = 1000000011

join_room.room_id = resp.room_id

join_room.join = 1

clientB.send_message(join_room)


#cancel room

cancel_req = proto.MatchCancel()

cancel_req.uid = 100000010

clientA.send_message(cancel_req)

resp = ClientA.read(proto.MatchCancelResp.CMD)

assert resp

assert resp.error_code == 0


clientA, clientB就是实现定义的两个fixture,他们是两个不同的客户端,高速且有序的模拟了两个客户端的时序交互行为,即能自动化也满足交互时序,同时逻辑也非常清晰

你可能感兴趣的:(pytest,自动化测试,python,python,自动化,测试,pytest)