测试平台系列(122) 改造添加用例页面.md

2022/6/7 测试平台接口测试FastApiPythonReact

大家好~我是米洛
我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持。
欢迎关注我的公众号米洛的测开日记,一起交流学习!

# 回顾

上一节我们稍微优化了一下登录页,后面我们可以开始进一步开发了,以功能点为主。所以这一节我们要解决这样一个需求:

其实这个问题是因为http请求发出去的时候,并没有进行全局变量替换,但现在有个很严重的问题:

我们的case都是跨环境的,这就导致如果这里填了环境变量,但我并不知道该用哪套环境的数据,而且这里也漏了我们之前编写的base_path功能。

# 改造方案

这次的方案改动会很大,我个人觉得,新增和编辑页面完全区分开,比较不合适。而且不太友好,所以在添加case的时候,我会以弹窗的形式展示。

这样能够少跳转一次url,相对来说更友好一些。至于那么多操作条件,咱们就一并提交。

  • 用例信息

  • 前置条件

  • 后置条件

  • 断言

    组件我们是不能复用了,因为之前添加前置/后置/断言,都是在已有的用例上添加,现在都是暂存到前端,最后统一提交,没事我们慢慢来,迟早要走到这一步的。

# 编写后端代码

后端代码,我们需要做的有2部分:

  1. 批量提交用例数据
  2. 根据传入的数据在线执行case(供调试用,这点比较麻烦)

那我们就先从软柿子开捏吧,因为第二个部分可能还有些问题,我们可以放到后面去做。

首先我们要综合来看,新增是全部数据一并提交,而修改往往是只修改一部分,其实修改我们也可以改为批量提交,但是代价很大,因为要改的内容多,还是抽出比较合适。这样就会有一个问题了,我们的前置条件等表单,都是需要case_id字段的,所以这里我们为了新的模式,只能把它改成非必填了。(这点我还没有考虑好,只是初步的设定)

  • 修改各个表的form

asserts

测试数据

前后置条件

这里前后置条件本身就忘记加上这个约束了,所以也不太需要改,重点都是validator这个方法里面。

  • 编写大杂烩form 暂且叫TestCaseInfo

它结合了4种schema,我测试了发现,会根据里面的schema进行校验,所以我们的validator只需要校验case字段即可。

  • 改写insert_record方法

    在此之前,我们需要先改造下Mapper里面的insert_record方法,由于我们是批量插入,所以需要用一个事务去保证所有数据要么都插入要么都失败,之前我的设想是在router层创建session,并开启事务,接着把session投递到对应的插入操作中,所以我们需要改改insert方法:

如果ss(代表session)传入了,说明是一个事务操作,其实也可以改个名字叫transaction,但是因为我们还是需要拿到session信息的,所以暂时先这么写了。除了参数多了个ss以外,没有其他区别了。

  • 编写insert_test_case方法

    在TestCaseDao类编写insert_test_case方法,主要有3步:

  1. 判断该目录是否存在该case名
  2. 添加case
  3. 添加断言/前后置条件/测试数据
    @staticmethod
    async def insert_test_case(session, data: TestCaseInfo, user: int):
        """
        测试数据和用户id
        :param data: 测试用例数据
        :param session: 异步session
        :param user: 创建人
        :return:
        """
        query = await session.execute(
            select(TestCase).where(TestCase.directory_id == data.case.directory_id, TestCase.name == data.case.name,
                                   TestCase.deleted_at == 0))
        if query.scalars().first() is not None:
            raise Exception("用例名称已存在")
        cs = TestCase(**data.case.dict(), create_user=user)
        # 添加case,之后添加其他数据
        session.add(cs)
        await session.flush()
        session.expunge(cs)
        # # 添加断言
        for a in data.asserts:
           a.case_id = cs.id
           a = TestCaseAsserts(**a.dict(), user=user)
          await TestCaseAssertsDao.insert_record(a, ss=session)
        
        # 添加构造条件
        for c in data.constructor:
            c.case_id = cs.id
            c = Constructor(**c.dict(), user=user)
            await ConstructorDao.insert_record(c, ss=session)
        
        # 添加测试数据
        for t in data.data:
            t.case_id = cs.id
            t = PityTestcaseData(**t.dict(), user=user)
            await PityTestcaseDataDao.insert_record(t, ss=session)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

思路比较简单,但是有没有觉得代码很冗余,差不多的步骤写了3遍。所以这时候我们就可以把重复的代码剥离出来,组成一个新的方法,来看看:

    @staticmethod
    async def _insert(session, case_id: int, user: int, form: TestCaseInfo, **fields: tuple):
        for field, model_info in fields.items():
            md, model = model_info
            field_data = getattr(form, field)
            for f in field_data:
                setattr(f, "case_id", case_id)
                data = model(**f.dict(), user=user)
                await md.insert_record(data, ss=session)

    @staticmethod
    async def insert_test_case(session, data: TestCaseInfo, user: int):
        """
        测试数据和用户id
        :param data: 测试用例数据
        :param session: 异步session
        :param user: 创建人
        :return:
        """
        query = await session.execute(
            select(TestCase).where(TestCase.directory_id == data.case.directory_id, TestCase.name == data.case.name,
                                   TestCase.deleted_at == 0))
        if query.scalars().first() is not None:
            raise Exception("用例名称已存在")
        cs = TestCase(**data.case.dict(), create_user=user)
        # 添加case,之后添加其他数据
        session.add(cs)
        await session.flush()
        session.expunge(cs)
        await TestCaseDao._insert(session, cs.id, user, data, constructor=(ConstructorDao, Constructor),
                                  asserts=(TestCaseAssertsDao, TestCaseAsserts),
                                  data=(PityTestcaseDataDao, PityTestcaseData))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

我们重新定义一个_insert方法(内部用的),接着把要调整的字段都用参数传入进去,这里比较好的一点是,我们的表定义都一致,比如case_id都叫case_id,创建/修改用户都叫user,否则我们的参数还得额外设计一下。

  • 编写添加接口
# v2版本创建用例接口
@router.post("/create", summary="创建接口测试用例")
async def create_testcase(data: TestCaseInfo, user_info=Depends(Permission()), session=Depends(get_session)):
    async with session.begin():
        await TestCaseDao.insert_test_case(session, data, user_info['id'])
    return PityResponse.success()
1
2
3
4
5
6

# 这里我没有用/v2/testcase/create这样的路由,因为v2的话,肯定是大改版了,暂时就先这样叫。注意我这里没有再用try包裹代码了,因为我们的http中间件已经自动做了异常捕获,所以以后也都不需要这么麻烦了。

由于时间的关系,我们就暂时扩展到这里,随便测试一下:

发现没啥问题(其实中间的问题我都搞定了),收工~