自动化测试平台(九):用例概览页开发和子级菜单的使用

x33g5p2x  于2021-12-28 转载在 其他  
字(5.6k)|赞(0)|评价(0)|浏览(669)

一、前言

上一章我们完成了列表组件公共化封装和项目管理功能的实现,这一章将实现用例概览页功能,用于测试项目的测试情况的一个统计展示。

为了开发效率,很多类型都是用any来定义的,小伙伴可以自己进行完善

完整教程地址:《从0搭建自动化测试平台》

项目在线演示地址:http://121.43.43.59/ (帐号:admin 密码:123456)

本章内容实现效果如下:

二、题外话

之前有小伙伴反应多次点击会出现消息重复弹出的情况,如下图所示:

我们可以修改antd的message组件的配置来设置它的显示最大数量、位置、持续时间等,例如下面的代码:

  1. import { message } from 'antd';
  2. message.config({
  3. top: 100, //位置
  4. duration: 2,//持续时间
  5. maxCount: 1,//显示的最大数量
  6. });

app.tsx中加入上述代码即可。

三、后端用例模块接口

1. 项目文件创建及表模型建立

1)首先在代码项目下执行创建用例模块的命令:

  1. django-admin startapp cases

后面用例相关(用例模块、报告等)都会放到这里面进行管理。

2)建立用例模块表,表模型如下:

  1. from django.db import models
  2. from comFunc.comModel import ComModel
  3. from project.models import Project
  4. class Module(ComModel):
  5. id = models.AutoField(primary_key=True)
  6. name = models.CharField(max_length=32, verbose_name="模块名称")
  7. status0 = models.IntegerField(default=0, verbose_name="等待执行")
  8. status1 = models.IntegerField(default=0, verbose_name="执行失败")
  9. status2 = models.IntegerField(default=0, verbose_name="正在执行")
  10. status3 = models.IntegerField(default=0, verbose_name="执行完成(接口用例特有状态)")
  11. status4 = models.IntegerField(default=0, verbose_name="执行通过")
  12. status = models.IntegerField(verbose_name="模块状态")
  13. parent = models.ForeignKey(to='self', verbose_name="父模块", null=True, on_delete=models.DO_NOTHING)
  14. project = models.ForeignKey(to=Project, on_delete=models.DO_NOTHING, default=1, verbose_name="关联的项目id")
  15. type = models.IntegerField(verbose_name="模块类型")
  16. class Meta:
  17. verbose_name = '用例模块'
  18. db_table = 'module'

类似的增删改查代码根据之间的教学进行CURD开发即可。

2. 概览列表接口开发

1)概览列表接口的功能是将项目下模块不同状态数量进行查询并汇总,没有模块的项目默认全部为0,我们可以先查项目表,然后使用django里orm的反向查询它的模块数据,没有模块的全部设为0即可,代码如下:

  1. def case_overview(request):
  2. """ 用例概览情况 """
  3. case_type = request.query_params['type'] # 代表查询UI还是API用例概览情况
  4. projects = Project.objects.filter(type=case_type)
  5. value_fields = {f'status{i}': Sum(f'status{i}') for i in range(5)}
  6. value_fields.update(
  7. {'project_name': F('project__name'), 'project_status': F('project__status'),
  8. 'total': F('status0') + F('status1') + F('status2') + F('status3') + F('status4')})
  9. res_data = []
  10. for project in projects:
  11. data = project.module_set.filter(parent_id=None).values('project_id').annotate(**value_fields).first()
  12. res_data.append(data or {**{f'status{i}': 0 for i in range(5)},
  13. **{'project_id': project.id, 'project_status': project.status,
  14. 'project_name': project.name, 'total': 0}})
  15. return Response(data=res_data)

这样的实现性能上会查一些,因为每个项目都会进行一次模块的反查,这样每多一个项目就会多一次sql的查询。
更好的做法是直接将两张表的数据都查出来,然后代码中做组合,小伙伴们可以自己动手去实现。

2)按照上面实现的概览接口使用时会报错,这是因为返回的是queryset类似list的这种类型的数据,我们之前开发的中间件没有适配这种情况,需要修改中间件来进行兼容,修改后的中间件核心代码如下:

  1. if res_data is not None:
  2. if isinstance(res_data, dict):
  3. if 'code' not in res_data:
  4. if 'data' in res_data:
  5. response.data.update({'code': status.HTTP_200_OK, 'msg': ''})
  6. else: # delete请求返回的数据没有data,所以下面的逻辑需要单独处理
  7. response.data = {'code': status.HTTP_200_OK,
  8. 'msg': res_data.pop('msg', ''),
  9. 'data': res_data}
  10. elif isinstance(res_data, (list, QuerySet)):
  11. response.data = {'code': status.HTTP_200_OK, 'data': res_data, 'msg': ''}

注:需要在头部引入QuerySet:

  1. from django.db.models import QuerySet

四、前端页面

首先,还是执行创建cases页面模块的文件:

  1. npx umi g page cases/index --typescript

1. 子级菜单

根据演示效果我们可以看到,用例管理是一个有子级的菜单。

这里我们只需要在.umirc.ts配置文件加入嵌套routes即可,代码如下:

  1. {
  2. name: '用例管理',
  3. icon: 'case',
  4. path: '/case',
  5. routes: [
  6. {
  7. name: '自动化UI用例管理',
  8. path: '/case/uiCaseOverview',
  9. component: '@/pages/caseOverview',
  10. },
  11. {
  12. name: '自动化Api用例管理',
  13. path: '/case/apiCaseOverview',
  14. component: '@/pages/caseOverview',
  15. },
  16. ],
  17. },

这样修改完成后我们就能够在页面上看到如下的菜单效果了:

2. 概览页实现

概览页使用了antd栅格布局来排版,然后结合Card组件实现了如下的效果:

最后结合请求的路由地址不同给请求概览页接口带上不同的type参数,将其返回的数据按上述组件格式进行遍历添加,就能够实现最终的效果了:

完整index.tsx代码如下:

  1. import React, { useState, useEffect } from 'react';
  2. import { PROJECT_STATUS_TAG } from '@/globalEnum';
  3. import { caseOverview } from '@/services/cases';
  4. import { Card, Col, Spin, Tag, Button, Badge, message, Row } from 'antd';
  5. const CaseOverview: React.FC = (props: any) => {
  6. const type = props.match.path === '/case/uiCaseOverview' ? 1 : 2;
  7. const [loading, setLoading] = useState<boolean>(true);
  8. const [cardItems, setcardItems] = useState<any>([]);
  9. useEffect(() => {
  10. caseOverview({ type: type }).then((res) => {
  11. const cards = res.data.map((item: any) => (
  12. <Col key={item.project_id} span={8}>
  13. <Card
  14. title={
  15. <div>
  16. <h3 style={{ fontWeight: 'bold', display: 'inline' }}>
  17. {item.project_name}
  18. </h3>
  19. <Button
  20. style={{ float: 'right' }}
  21. onClick={() => message.warning('待实现')}
  22. >
  23. 管理UI元素
  24. </Button>
  25. </div>
  26. }
  27. >
  28. <Tag
  29. style={{ float: 'right', height: '2em' }}
  30. color={PROJECT_STATUS_TAG[item.project_status].color}
  31. >
  32. {PROJECT_STATUS_TAG[item.project_status].text}
  33. </Tag>
  34. <ul style={{ listStyleType: 'none', padding: 0 }}>
  35. <Badge status="error" text="用例失败数量:" />
  36. {item.status1}
  37. <li>
  38. <Badge status="default" text="等待执行数量:" />
  39. {item.status0}
  40. </li>
  41. <li>
  42. <Badge status="warning" text="正在执行数量:" />
  43. {item.status2}
  44. </li>
  45. <li>
  46. <Badge status="processing" text="执行完成数量:" />
  47. {item.status3}
  48. </li>
  49. <li>
  50. <Badge status="success" text="测试通过数量:" />
  51. {item.status4}
  52. </li>
  53. </ul>
  54. <h3 style={{ float: 'right' }}>用例总数量:{item.total}</h3>
  55. </Card>
  56. </Col>
  57. ));
  58. setcardItems(cards);
  59. setLoading(false);
  60. });
  61. }, []);
  62. return (
  63. <Spin spinning={loading}>
  64. <div
  65. style={{
  66. overflowX: 'hidden',
  67. overflowY: 'auto',
  68. height: 'calc(100vh - 100px)',
  69. }}
  70. >
  71. <Row gutter={[16, 16]}>{cardItems}</Row>
  72. </div>
  73. </Spin>
  74. );
  75. };
  76. export default React.memo(CaseOverview);

globalEnum.ts新增代码如下:

  1. export const PROJECT_STATUS_TAG = {
  2. 0: { text: '项目进行中', color: '#faad14' },
  3. 1: { text: '项目已完成', color: '#52c41a' },
  4. }; //代表请求列表

cases服务层接口请求代码如下:

  1. import { request } from 'umi';
  2. export function caseOverview(params: any) {
  3. return request<any>('/cases/overview', { params });
  4. }

五、总结

这一章没有复杂的内容,基本都是重复的CURD。下一章节会正式开始自动化用例模块相关的教学和开发了。

平台在线演示地址:http://121.43.43.59/ (帐号:admin 密码:123456)

相关文章