借助vue-element-admin定制化构建测试平台

写在前面

这篇文章适用于使用过Vue和Django的同学,文章介绍如何搭建vue-element-admin框架,通过实例介绍,如何从vue-element-admin挑选适用于自己项目的页面,这样测试开发人员可以更加集中于测试需求的开发,文章最后介绍了处理队列的python库celery,用于异步执行任务。它将任务添加到Celery队列中,使应用程序可以继续执行其他操作,而不必等待任务完成。这个方法通常用于处理比较耗时的任务,以避免对应用程序的性能造成影响。


基础环境搭建

vue-element-admin框架介绍

Vue-Element-Admin是一个基于Vue和Element-UI实现的后台管理系统快速开发框架。该框架借助了Element-UI组件库以及Vue的数据驱动视图特性,极大地简化了后台管理系统的开发过程。
Vue-Element-Admin框架提供了大量的实现后台管理系统所需的通用组件和功能,例如路由管理、用户管理、权限管理等。同时,该框架还提供了多个主题风格、国际化以及Chrome调试工具等实用特性,为后台管理系统的开发提供了更多的扩展和自定义选项。
Vue-Element-Admin框架使用Webpack打包工具进行构建,并对Webpack的构建方式进行了优化,同时该框架还支持ES6、ES7的新特性,并提供了一系列的代码检查和格式化工具,以保证代码的质量和规范性。
GitHub地址:https://github.com/PanJiaChen/vue-element-admin
项目在线预览:https://panjiachen.gitee.io/vue-element-admin

vue-element-admin环境搭建

  1. 直接下载 https://gitee.com/constfiv/vue-element-admin-fix-install-problem,相比于https://github.com/PanJianChen/vue-element-admin.git的版本,这个版本好很多,npm intsall不会有各种依赖的问题,而且时间快很多
  2. 重装node,安装node低版本,我装的是12的版本 https://nodejs.org/zh-cn/download/releases/
  3. cmd -> npm install,大概几分钟时间
    在这里插入图片描述
  4. 输入npm run dev,项目成功启动
    借助vue-element-admin定制化构建测试平台_第1张图片

vue-element-admin与Django集成

与真实环境使用Nginx相比,更建议将打包好的文件,放到DJango的Template目录下,方便调试
使用npm run build:prod 开始build包
借助vue-element-admin定制化构建测试平台_第2张图片
打包后的文件放到Django项目目录下重命名为frontend
借助vue-element-admin定制化构建测试平台_第3张图片
修改settings中TEMPLATES和STATICFILES_DIRS

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        ## 指向frontend
        'DIRS': [os.path.join(BASE_DIR, 'frontend')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
STATICFILES_DIRS = [
    ## 指向frontend/static
    os.path.join(BASE_DIR, "frontend/static")
]

配置路由,访问项目,结果如下
借助vue-element-admin定制化构建测试平台_第4张图片

定制开发项目

平台开发方案制定

jenkins流水线构建是自动化测试的一个重要环节,如果自动化没有实现流水线,这个自动化不能算得上成功,但是很多企业jenkins没有对外开放,只有少数人有访问的权限,而且jenkins可能部署在多个服务器,没有办法将需要使用的job汇总在一起展示,需要登陆多个平台。同时jenkins显示自动化每个节点信息都很友好。鉴于需要使用python语言,所以最后确定利用vue实现前端界面,利用django实现后台服务,Python调用jenkins job 实现自动测试任务调度的方案。

平台开发实战

jenkins服务启动

首先确保本地搭建了jenkins服务,保证后端服务可以调通

  1. 确保本地已经搭建好了java的环境
  2. 去jenkins官网下载war包 https://www.jenkins.io/zh/download/
  3. 上传到linux上启动war包,并启动服务nohup java -jar jenkins.war --httpPort=8080 &
    在这里插入图片描述
  4. 注册账号并登陆
    借助vue-element-admin定制化构建测试平台_第5张图片

jenkins和Django集成

开发过程中可以前后端同时进行最后集成在一起,本例先开发后端服务,保证需求可以实现

  1. 安装python调用jenkins的第三方库 pip install python-jenkins
  2. 新建app,并加入到setting中
  3. 编写调用jenkins的代码,并测试,如下展示了调用jenkins的部分代码
import time
import requests
import jenkins


class JenkinsClient(object):
    def __init__(self):
        self.jenkins_config = {
            "url": "http://120.25.253.110:8080/",
            "username": "*****1688",
            "password": "******5588"
        }
        self.jenkins_server = jenkins.Jenkins(**self.jenkins_config)

    def get_all_jobs(self):
        '''
        获取所有JOBS
        '''
        return self.jenkins_server.get_all_jobs()

    def get_job_info(self, job_name):
        return self.jenkins_server.get_job_info(job_name)
    
    
    def get_all_jobs_info(self):
        '''
        获取所有JOB信息
        '''
        jobs_list = []
        jobs = self.get_all_jobs()
        for job in jobs:
            jobs_dict = {}
            job_name = job.get('name')
            number = self.get_lastbuild_number(job_name)
            url, timestamp, result = self.get_build_job_info(job_name, number)
            test_total, tests_skip, tests_faild = self.get_job_testcase(job.get('url'), number)
            jobs_dict['build_job'] = job_name
            jobs_dict['build_url'] = url
            jobs_dict['build_time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp / 1000))
            jobs_dict['build_number'] = number
            jobs_dict['build_result'] = result
            jobs_dict['testcase_total'] = test_total
            jobs_dict['testcase_skips'] = tests_skip
            jobs_dict['testcase_failure'] = tests_faild
            print(jobs_dict)
            jobs_list.append(jobs_dict)
        return jobs_list
  1. 开发DJango接口,可参考 https://blog.csdn.net/m0_48468018/article/details/129640549

3.1 开发数据模型

class JenkinsJobModel(models.Model):
    objects = models.Manager()
    build_job = models.CharField('任务名称', max_length=255)
    build_number = models.IntegerField('构建编号', default=0)
    build_time = models.DateTimeField('构建时间')
    build_url = models.URLField('构建URL', max_length=500)
    build_result = models.CharField('构建结果', max_length=50)
    testcase_total = models.IntegerField('用例总数', default=0)
    testcase_skips = models.IntegerField('用例跳过数', default=0)
    testcase_failure = models.IntegerField('用例失败数', default=0)
    created_at = models.DateTimeField('创建时间', default=datetime.datetime.now)
    updated = models.DateTimeField('更新时间', auto_now=True)

    def __str__(self):
        return f'{self.build_job}-{self.build_number}'

3.2 同步数据库,python manage.py makemigrationspython manage.py migrate
借助vue-element-admin定制化构建测试平台_第6张图片

3.3 编写serializers代码

class JenkinsJobModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = JenkinsJobModel
        exclude = [
            'created_at'
        ]

3.4 编写视图代码

client = JenkinsClient()

@api_view(['GET'])
def get_all_jobs(request):
    '''
    获取所有任务,首次手工将JENKINS同步到测试管理平台
    '''
    search = request.GET.get('title')
    querysets = JenkinsJobModel.objects.all()
    if not querysets:
        print("表中无记录,则重新调用Jenkins API同步到Django")
        jobs = client.get_all_jobs_info()
        for job in jobs:
            JenkinsJobModel.objects.get_or_create(**job)
        else:
            print(f"总共同步创建{JenkinsJobModel.objects.count()}条JOB记录!")
            querysets = JenkinsJobModel.objects.all()
    elif search:
        querysets = JenkinsJobModel.objects.filter(Q(build_job__icontains=search))
        print(f"按条件搜索匹配{querysets.count()}条记录!")
    else:
        querysets = JenkinsJobModel.objects.all()
    serializer = JenkinsJobModelSerializer(querysets, many=True)
    return Response(serializer.data, status=status.HTTP_200_OK)

3.5 编写路由并启动服务,测试后端服务是否符合预期
借助vue-element-admin定制化构建测试平台_第7张图片

从vue-element-admin中找到合适的前端代码,定制修改并与后端代码联调

  1. npm run dev 启动vue-element-admin服务,并浏览页面找到合适自己使用的页面,如下是我想要的页面,对模块不熟悉可以借助官方文档https://panjiachen.github.io/vue-element-admin-site/zh/guide
    借助vue-element-admin定制化构建测试平台_第8张图片
  2. 浏览代码的路由,并逐步定位到所属的模块,如table先从router中找到tableRouter,然后定位到@/views/table/complex-table
    借助vue-element-admin定制化构建测试平台_第9张图片
  3. 复制模块的代码和相关联的其他组件的代码到自己的项目中
  4. 根据自己的需求更改代码,如下是我对table部分的代码修改,修改比较简单,主要根据自己写好API返回的值,进行集成
   <el-table
      :key="tableKey"
      v-loading="listLoading"
      :data="joblist"
      border
      fit
      highlight-current-row
      style="width: 100%;"
      @sort-change="sortChange"
    >

      <el-table-column label="序号" prop="id" sortable="custom" align="center" width="50" :class-name="getSortClass('id')">
        <template slot-scope="scope">
          <span>{{ scope.row.id }}</span>
        </template>
      </el-table-column>

      <el-table-column label="任务名称" align="center">
        <template slot-scope="scope">
          <span class="link-type" @click="handleUpdate(scope.row)">{{ scope.row.build_job }}</span>
        </template>
      </el-table-column>

      <el-table-column label="构建编号" align="center" width="100">
        <template slot-scope="scope">
          <span>{{ scope.row.build_number }}</span>
        </template>
      </el-table-column>
      <el-table-column label="构建时间" width="170px" align="center">
        <template slot-scope="scope">
          <span>{{ scope.row.build_time}}</span>
        </template>
      </el-table-column>

      <el-table-column label="构建结果" width="110px" align="center">
        <template slot-scope="scope">
          <span>{{ scope.row.build_result }}</span>
        </template>
      </el-table-column>

      <el-table-column label="用例总数" width="110px" align="center">
        <template slot-scope="scope">
          <span v-if="scope.row.testcase_total>0" style="color:red;">{{ scope.row.testcase_total }}</span>
          <span v-else>{{ scope.row.testcase_total }}</span>
        </template>
      </el-table-column>

      <el-table-column label="用例跳过数" width="110px" align="center">
        <template slot-scope="scope">
          <span v-if="scope.row.testcase_skips>0" style="color:red;">{{ scope.row.testcase_skips }}</span>
          <span v-else>{{ scope.row.testcase_skips }}</span>
        </template>
      </el-table-column>

      <el-table-column label="用例失败数" width="110px" align="center">
        <template slot-scope="scope">
          <span v-if="scope.row.testcase_failure>0" style="color:red;">{{ scope.row.testcase_failure }}</span>
          <span v-else>{{ scope.row.testcase_failure  }}</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" min-width="100" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button type="success" size="mini">
            <a :href="scope.row.build_url" target="_blank" style="text-decoration: none">
              跳转
            </a>
          </el-button>
          <el-button type="primary" size="mini" @click="handleBuild(scope.row.build_job)">
            构建
          </el-button>
          <el-button type="info" size="mini" @click="handleUpdate(scope.row)">
            编辑
          </el-button>
          <el-button type="danger" size="mini" @click="handleUpdate(scope.row)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  1. 启动Vue和Django服务,vue调用django提供的接口,前后端调试,保证符合自己的需求没有问题产生,调试过程中注意使用console.log()协助定位问题,查看浏览器的Console面板查看问题,可以调整前端和后端保证调通。
    借助vue-element-admin定制化构建测试平台_第10张图片
  2. npm run build 到最后部署的时候,可以使用nginx整合前后端,监听前端接口转发给后端服务,也可以将dist的目录直接配置到django的静态文件夹下,注意在setting文件中指定静态文件的路径,在路由中可以 path('', TemplateView.as_view(template_name="index.html"))直接将vue的index也页面作为主页面和配置re_path(r'^static/(?P.*)$', static.serve, {'document_root': settings.STATIC_ROOT})全局提供静态资源服务
urlpatterns = [
    path('admin/', admin.site.urls),
    path('autotest/', include("autotest.urls")),
    path('', TemplateView.as_view(template_name="index.html")),  ## 这里将url的根路径指向vue中的index页面
    path('api-auth/', include('rest_framework.urls')),
    re_path(r'^static/(?P.*)$', static.serve,
             {'document_root': settings.STATIC_ROOT}, name='static'),
]

增加异步和定时任务

为什么使用异步

以购买机票为例,通常来说,当我们购票时,机票系统需要与航空公司的服务器进行交互,查询座位信息、价格等,因为交互过程可能比较耗时,因此一般是通过异步方式来实现。

具体来说,在购买机票时,我们通常会提交一张订单,然后等待系统返回确认信息。在这个过程中,我们可能会看到一个“正在处理中”的提示,这实际上是机票系统在与航空公司的服务器交互、获得确认信息的时候所调用的异步接口。

异步接口工作的原理是在发起请求后并不立即返回结果,而是在后台继续执行其他任务(如查询其他座位信息),然后再把结果返回,这样就能避免阻塞主线程。

因此,异步的实现能够提高机票系统的处理能力和效率,并且增加用户体验,使用户可以更快速地购买机票。

Celery

什么是Celery

Celery是一个Python开发的分布式任务队列,它可以处理大量任务并且具有较高的可靠性。它本质上是一个任务调度器,将任务发送到工作队列中,由一组工人异步地处理这些任务。最常见的使用方法是将它与Django、Flask、Bottle等Web框架结合使用,Celery可以实现异步处理任务、延迟任务执行等功能。

Celery的工作原理基于三级模型,即任务发布者、消息中间件和任务执行者。在这个模型中,任务发布者将任务传递给消息中间件(如RabbitMQ,Redis等),然后任务执行者从中间件中获取到消息,并执行其中的任务。

Celery具有许多应用场景,如:

  1. 异步处理业务逻辑

  2. 延迟执行一些任务,比如延迟消息推送

  3. 后台定时任务,如定时清理日志

  4. 异步执行复杂的计算任务

  5. 分布式爬虫,多个爬虫进行协同工作,一个负责解析url,另外一个负责提取数据信息
    借助vue-element-admin定制化构建测试平台_第11张图片

Celery的框架组成及原理

Celery是一个开源的分布式任务队列,其由以下组件构成:

  1. Broker: Broker是Celery中最重要的组件,它负责任务的调度和分发。Celery支持多种Broker如:Redis、Rabbitmq、Amazon SQS等。
  2. Worker: Worker是处理任务的工作进程,它从Broker中获取待执行的任务,然后将结果返回给Broker。
  3. Task: Task是为异步执行的函数提供装饰器的实例。当Task被调用时,它将被发送到Broker以便分配给Worker执行。
  4. Producer: Producer是负责生成Task对象并将它发布到Broker的组件。
  5. Beat: Beat是Celery自带的调度器组件,其功能类似于Crontab。可以用来调度定时任务,Producer和Beat配合使用分发给Broker。

Celery的原理如下:

  1. 应用程序将待处理的任务发送到消息代理(Broker)中。
  2. Celery Worker从消息代理中获取任务,并将其分配给Worker进程。
  3. Worker进程执行任务,结果发送回Broker以供查询。
  4. 应用程序从消息代理中获取结果。

这个过程中,消息代理充当了任务分配器和数据传输媒介的角色。 任务可以是任何Python可调用函数或类方法,并且可以在任何数量的节点上并行运行以实现扩展性。
借助vue-element-admin定制化构建测试平台_第12张图片

Celery环境搭建

  1. celery安装
pip install celery
pip install eventlet (windows必装)
  1. regis安装 pip install redis
  2. celery监控flower pip install flower

Celery windows环境为什么用到eventlet?
在 Windows 环境下,Celery 默认使用了 eventlet 作为任务执行的协程库,是因为 Windows 平台下没有像 Linux 平台上的 gevent 这样的高效协程库。
Eventlet 是一种 Python 协程库,它提供了简单易用的 API,使得编写异步代码变得简单。Celery 使用 eventlet 作为后台执行器来利用 Windows 上的并发优势,可以提高同步函数和方法的执行速度,从而避免阻塞现象产生,提高 Celery 的任务执行效率。

在Python代码中使用Celery

以下是使用Celery+Redis的Python代码中实例化Celery对象的示例:

  1. 创建一个celery_task.py文件,并贴入如下代码
from celery import Celery
import time

app = Celery('tasks', broker='redis://127.0.0.1:6379/0',
             backend='redis://127.0.0.1:6379/1')


@app.task
def send_email(name):
    print("向%s发送邮件..." % name)
    time.sleep(5)
    print("向%s发送邮件完成" % name)
    return "ok"


@app.task
def send_msg(name):
    print("向%s发送短信..." % name)
    time.sleep(5)
    print("向%s发送短信完成" % name)
    return "ok"
  1. 创建一个celery_demo.py文件作为一个测试文件
from celery_task import send_email, send_msg

# celery_task 是创建的异步执行文件

if __name__ == '__main__':
    for i in range(1, 10):
        result1 = send_email.delay("张三")
        print(result1.id)
        result2 = send_email.delay("李四")
        print(result2.id)
        result3 = send_msg.delay("王五")
        print(result3.id)
        result4 = send_msg.delay("赵六")
        print(result4.id)

在这里,我们先使用 Celery() 函数创建一个 app 实例,其中将 Celery 实例命名为 tasks。链接到指定的Redis消息代理和结果存储后端。

在这里使用了 Redis 作为我们的消息代理和结果后端,使用地址 redis://localhost:6379/0,其中 0 表示 redis 中第一个数据库,如果你需要更多数据这点可以根据自己的需求进行设置。之后我们用 @app.task 来定义任务。

然后测试代码中启动队列运行

  1. 最后启动服务

启动celery调度,确保启动的时候可以找到celery_task,可以切换到对应的目录下启动

celery -A celery_task  worker -l info -P eventlet

启动flow服务进行监控,监控的数据库需要和celery_task代码中的保持一致,可以监听到对应的数据

celery --broker=redis://127.0.0.1:6379/0 flower --address=0.0.0.0 --prot=5555

如图是运行test后flower显示
借助vue-element-admin定制化构建测试平台_第13张图片

Django环境中接入celery

  1. 配置Django项目

在Django项目的settings.py文件中添加以下配置,其中myproject为项目名:

CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0'  # Redis作为消息中间件
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'  # Redis作为任务结果存储
CELERY_TIMEZONE = "Asia/Shanghai"  # 指定时区,不指定默认为 'UTC'
  1. 创建Celery实例对象

在Django项目的根目录下,创建一个名为celery.py的文件,添加以下代码:

import os
import django
from celery import Celery
from myproject import settings

# 设置环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django.setup()
# 创建Celery实例对象
app = Celery('myproject')
# 指定中间人
app.config_from_object('django.conf:settings', namespace='CELERY')
# 自动注册任务
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
  1. 项目的init文件中加入如下代码:
__all__ = ['app']

借助vue-element-admin定制化构建测试平台_第14张图片

  1. 在app中创建tasks任务

在Django项目的某个app中创建tasks.py文件,一定是该文件,符合celery的编码规范,并编写任务代码:

from celery import shared_task

@shared_task
def add(x, y):
    return x + y
  1. 添加路由和并在views.py中调用celery任务
from django.http import HttpResponse
from .tasks import *
def task_add_view(request):
    add.delay(100, 200)
    return HttpResponse(f'调用函数结果')
  1. 启动Celery服务

使用以下命令来启动Celery服务:

celery -A django项目名 worker -l info -P eventlet

其中celery_client为Celery实例所在的模块,worker表示启动一个工作进程,`-l表示日志等级。

  1. 启动django服务,并进行访问,启动该任务
    借助vue-element-admin定制化构建测试平台_第15张图片

如果调度策略要改成定时任务,则可以在settings指定,添加如下代码

CELERY_BEAT_SCHEDULE = {
    'add-every-30-seconds': {  # 任务名字,随意
        'task': 'api.tasks.add',  # celery任务,这里一定是完整的路径,否则报错
        'schedule': 30.0,  # 定时时间
        'args': (16, 16)  # 需要传递到 task 的参数
    },
}

借助vue-element-admin定制化构建测试平台_第16张图片

并启动beat服务

celery -A 项目名  beat -l info 

beat服务日志
在这里插入图片描述
celery服务日志
在这里插入图片描述

你可能感兴趣的:(前端,vue.js,django,前端)