【困惑】关于apiobject模式的接口框架设计

最近在搭公司项目的接口框架,想尝试下课程上教的apiobject这种设计模式。

试着写了一部分接口后,总感觉这种设计方式很难管理维护用例,而且感觉将所有接口单独封装,分层封装没有UI那么有意义,想让不懂代码的同事去上手也挺费劲。

之前学课程之前,我写接口框架采用的是用yaml文件来管理测试用例,将yaml文件解析出来直接跑case,这种方式对管理和维护用例来说非常方便。学了apiobject后,觉得自己反而不会写框架了,又想用这种设计,但是又没get到它到底有什么优点,很纠结,写起来很难受。

不知道是不是我没get到这种设计模式的应用场景,希望大佬能指点一下,这种设计模式到底有什么优点,如果能帮看下我框架的结构和设计问题就更好了,感谢~~~。

附上框架代码

https://github.com/ae86sen/api_framwork

1 个赞

这种主要是在与分层组合。举个例子,我一个场景就是电商的查商品,下单,新增收货地址,那么我就可以调用商品model类(包括商品的增删改查)的查方法,订单类的下单,地址类的增加收货地址方法,组合成一个场景,其他场景也类似,如果你只用单接口,那么用不用apiobject无关紧要

那我框架又想测单接口,又想测场景怎么搞

那就测呗,直接调用model的方法,一个方法一个测试用例啊,场景说明白了,单接口又懵了,这。。。。。

这个我知道,我现在就这么写的。我想说的是这种方式很难管理维护用例,怎么设计可以即方便维护管理用例,又能测单接口,又能方便测多接口场景的~

这个维护本身不存在说哪里很难管理测试用例的。我猜想你是想基于yaml驱动来吧,我不清楚python班老师有没有讲过基于apiobjcet的关键字框架驱动

是的,说到根儿上了,这部分内容只是录播课简单讲了下,大佬有空能帮我看下我框架的问题么?总感觉有点乱,我传了github地址

你去理解那种框架的思想,其实就很容易按照你自己的框架写个出来

api object模式上page object设计模式在接口测试上的一种延伸。因为ui自动化测试面临较多变更,所以page object模式的价值比较大。但是接口层相对稳定,封装po的价值并不明显。它主要适合如下场景

  • 一套api有多种实现,比如json接口、xml接口、rpc接口,想用相同的用例测试不同的接口实现
  • 接口本身也在经常变更,所以需要增加一层抽象封装
  • 对接口有较多的处理,比如加解密已经通用分析,把加解密可以封装到api object内
  • 有较多的字段需要填充,比如各种header token和默认字段,直接写http内容加多

如果只是简单的接口,并没有什么变动,那么就没必要增加一层抽象。

使用yaml与使用api object并不冲突,yaml和python是一个对比,用什么样的方式去维护用例,编程api还是数据模式。使用yaml也可以结合api object的。比如我们内部的测试框架中,就有一个例子

测试用例只包含业务
testcase.yaml

login:
  username: user1
  password: user2

po文件使用yaml,包含了具体的requests接口的发送过程和参数传递
po.yaml

login:
   - params:
     username: ''
     password: ''
  - requests.post:
      url: xxxx.json
      json:
        username: ${username}
        password: ${password}

两者接口也是一个完整的api object的例子。所以yaml与编程其实是一个对比层次。他们都可以依赖于api object。数据驱动的缺点就是丢失了编程IDE的强大类型推导能力。

总体建议是走封装,设计好业务用例api。这个是核心。在测试用例中直接塞进去各种http协议的填充过程,会导致你的用例慢慢的丢失重心。测试用例要围绕业务进行。 po要围绕实现进行。分层可以让用例更优雅。

当然我不知道你是不是实现思路有问题,你可以show下例子

我在上面附上了框架的github地址,麻烦思寒大佬有时间看看,指导一下

问题真多, 首先你的数据就保存的不太对。很多重复的内容,需要利用参数化生成


  - StartDate: '$startdate_yesterday'
    EndDate: '$enddate_yesterday'
    AppName: ''
    type: '系统启动'

  - StartDate: '$startdate_yesterday'
    EndDate: '$enddate_yesterday'
    AppName: ''
    type: '心跳丢失'

你这个数据是应该自动生成,而不是这么写死。
比如我们内部的框架是这么设计的

#1v1字段对应
test_params_fail3:
  - params:
      username: [a, b, c]
      password: [a, e, c]
  - eq: ["${username}", "${password}"]

#正交法生成数据并参数化用例
test_params_正交4:
  - params:
      username: [u1, u2, u3]
      password: [p1, p2]
      email: [1, 2, 3, 4]
  - log: "${username}"
  - log: "${password}"

测试用例也没看出来业务逻辑。

然后是api object,你封装的也有问题

    def get_department_id(self):
        """获取部门id"""
        data = {
            "method": "get",
            "url": conf.get_str('env', 'url') + conf.get_str('api', 'department_id') + self.get_center_code()[0] + r'/all',
            "headers": {"Cookie": self.get_cookie()}
        }
        return self.jsonpath(self.send_http(data).json(),'$..code')

你发送请求后,数据里会有很多字段,你需要把这些数据保存起来,而不是直接丢掉结果,只保存其中的一个字段。所以逻辑上应该是发送http是个api, 然后再封装一个获取数据的api。比如

depart=Department()
depart.get(id=1234)
depart.get_id

这里是怎么让它自动生成呢,例子没看明白。yaml里始终是字符串,怎么转化让它进行正交呢

从脚本中读取yaml数据,然后自己用算法生成动态数据。然后再传递给参数化用例。讲课的时候用的是yaml数据直接传递到参数化,要支持正交法与笛卡尔积法,就需要在中间插入这个数据生成过程。

发送http和获取字段的api,我封装成baseapi的方法可以么

还有这个地方的变量引用又该怎么实现呢

你现在不就是已经用了这个技术吗, 我看你的数据里已经有了变量

  - StartDate: '$startdate_yesterday'
    EndDate: '$enddate_yesterday'
    AppName: ''
    type: '心跳丢失'

所以还是用模板替换技术么,我以为有别的方法?

字符串模板替换、jinja模板、eval函数都可以

谢谢思寒~我去重构一下框架~顺便问下,学院的框架啥时候能开源呢?这部分内容课上就讲了一个小demo,想学习下整体的架构设计