teprunner测试平台入门级部署手册发布啦
很多朋友是因为teprunner,也就是这个小众的pytest内核测试平台关注的公众号。为了让大家更好的上手teprunner,我更新了它的README,希望能让小伙伴们根据这些文档内容,一步一步的在自己本地电脑上把项目跑起来。项目跑起来之后,就可以参考前面一系列的学习教程,自己动手做一遍,在做的过程中和teprunner进行对比,不懂的点逐一突破,由点到面,完整实现。**这种学习方式能更快速的掌
很多朋友是因为teprunner,也就是这个小众的pytest内核测试平台关注的公众号。为了让大家更好的上手teprunner,我更新了它的README,希望能让小伙伴们根据这些文档内容,一步一步的在自己本地电脑上把项目跑起来。项目跑起来之后,就可以参考前面一系列的学习教程,自己动手做一遍,在做的过程中和teprunner进行对比,不懂的点逐一突破,由点到面,完整实现。**这种学习方式能更快速的掌
经过Python测试交流群的小伙伴群策群力,teprunner添加了一个重要功能,把PyCharm中的代码,通过Git同步到测试平台中,生成测试用例。这样,teprunner就成了一个名副其实的pytest脚本在线管理平台。
teprunner测试平台已经有一个多月没有更新了,主要原因是定时任务不够稳定,经过反复试错,找到了解决办法,这次终于稳定了。
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 本文开发内容 在免费开源的测试平台中,teprunner的UI界面应该算得上第一梯度了,BUT,美中不足,在不同分辨率显示器(比如13.3寸小屏笔记本)下布局大的弹出窗口,表现不尽如人意。而这又是核心功能代码编辑的界面,比如用例编写、fixture编写,很有必要优化升级。除此之外还有滚动条的问题:竖向双滚动条和横向滚动条,也对体验造成影响。本文将对这些内容进行改造,具体如下: 编写用例页面,从弹窗改为子页面。 编写fixture页面,从弹窗改为子页面。 代码编辑区域高度自适应。 缩小左侧菜单,移动项目环境和新增按钮同一行,扩大编辑区面积。 消除所有双向滚动条。 消除所有横向滚动条。 语法说明添加环境变量和fixtures示例代码。 其他细节优化。 响应式是指同一套前端代码在不同浏览器(PC端、移动端等)都能适配,Element UI已经是响应式设计了,本文的响应式体现在代码编辑区行高根据window.innerHeight自动计算设定。 升级效果展示 左侧菜单栏变窄;添加环境变量和fixtures示例代码。 项目环境和新增按钮同一行显示,扩大有效内容可视面积。 fixtures的新增和编辑从弹窗改为子路由页面,代码编辑区高度自适应,纵向滚动条只出现一个。 消除横向滚动条;缩小了用例描述列以外其他列宽;替换操作按钮文字为图标。 用例的新增和编辑从弹窗改为子路由页面,代码编辑区高度自适应,纵向滚动条只出现一个。 用例运行结果的新增和编辑从弹窗改为子路由页面,代码编辑区高度自适应,纵向滚动条只出现一个。 测试计划的新增,编辑,运行结果也做了相同调整。 整体而言,有效信息的可视面积和可操作面积都尽可能扩大了! 关键技术点 前端代码修改文件如下: 比较繁琐,没有新知识,这里就不一一讲解了,而是选择用到的关键技术点来进行知识回顾: template添加<router-view></router-view> 主页面div添加v-if="$route.name === 'name'" 在router/index.js中添加children子路由 跳转页面前,存localStorage 新页面create()时,取localStorage this.$router.push跳转路由 div同行显示用float: left,紧跟的div换行用clear: both window.innerHeight获取屏幕高度 小结 本文完成了对teprunner测试平台前端UI界面的体验升级,已经适配13.3寸到27寸显示器,如果还在担心不好用的话,那么现在可以好好试试了。定时任务从目前优化效果来看,已经趋于稳定,不久就会和大家见面。 参考资料: Element UI图标按钮样式 https://www.jianshu.com/p/a81cd965943c
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 本文开发内容 上一篇文章已经把pytest引入到测试平台中,通过多线程和多进程的方式,运行测试用例。有了这个基础,做批量运行用例的功能就很简单了,只需要前端传入一个CaseList即可。本文的后端代码是增删改查和复用run_case相关代码做个run_plan。前端代码将学习如何通过LocalStorage在非父子组件之间传递数据。具体开发内容如下: 测试计划增删改查 测试计划动态添加移除测试用例 运行测试计划,批量运行用例 计划运行结果、用例运行结果 编写后端代码 编辑teprunner/urls.py文件,添加测试计划的路由: 编辑teprunner/models.py文件,添加3张表的model: Plan存放测试计划,PlanCase存放计划和用例关联关系,PlanResult和CaseResult类似,这里之所以把单独出来一张结果表,是为了和用例结果CaseResult相互独立,防止计划结果覆盖用例结果,毕竟CaseResult一般是手工跑出来的,PlanResult一般是定时任务跑出来的,功能数据解耦后处理起来也更方便。 编辑teprunner/serializers.py文件,添加序列化器: 为了前端展示需要,这里额外关联了一些字段进行展示。尤其是: caseNum用例总条数,passedNum成功条数,failedNum失败条数,errorNum错误条数。成功定义为:只有passed,没有failed和error的用例。失败定义为:只有failed,没有error的用例。错误定义为:只要有error的用例。 本文还展示了,如果序列化器的某个字段只在存库时使用,返回响应时不展示,该怎么处理: 重写to_representation方法,把不需要返回的字段pop移出去。这里output在计划运行结果不需要,在用例运行结果才需要,为了减少传输数据,就在计划运行结果的接口响应中移出去了。 最后来写View,新增teprunner/views/plan.py文件: 重写list方法,添加查询条件。重写destroy方法,判断必须先删除关联测试用例,才能删除测试计划。 PlanCaseView类似: 添加add方法: 在添加用例时写入plan_case表。添加remove方法: 在移除用例时从plan_case表删除数据。继续: result视图用来返回计划运行结果,根据searchType来分别查询用例总条数、成功条数、失败条数、错误条数。 继续: plan.case_result和case.case_result类似,返回测试计划的每条用例的运行结果。 在测试计划添加用例的时候,有个用例列表,需要增加2个过滤条件,编辑teprunner/views/case.py文件: 第一个过滤条件是排除当前计划已经添加过的用例:exclude_plan_id由前端传入,根据PlanCase关联关系找到已添加的用例进行过滤;第二个查询条件是关键字:用例ID或用例描述,跟PlanCaseView.list()里面一样。 最后,编辑teprunner/views/run.py文件,增加批量运行用例的功能: 添加delete_plan_result方法,在运行用例前,先把已有结果删掉,注意这里的model是PlanResult。继续: 修改pytest_subprocess函数,添加plan_id字段。继续: 修改save_case_result函数,判断没有plan_id就存case_result表,有plan_id就存plan_result表。 复制run_case的代码为run_plan: 修改case相关代码为plan。值得注意的是中间那段代码,根据plan_id从PlanCase中找到关联用例,生成了case_list,这样就复用run_case的多线程和多进程代码,批量运行测试用例了。 编写前端代码 编辑src/router/index.js文件,添加测试计划相关路由: { path: "plan", name: "plan", meta: { title: "测试计划", }, component: () => import("@/views/teprunner/plan/PlanManagement.vue"), children: [ { path: "addPlan", name: "addPlan", meta: { title: "添加计划", }, component: () => import("@/views/teprunner/plan/PlanEditor"), }, { path: "editPlan", name: "editPlan", meta: { title: "编辑计划", }, component: () => import("@/views/teprunner/plan/PlanEditor"), }, { path: "caseList", name: "caseList", meta: { title: "用例列表", }, component: () => import("@/views/teprunner/plan/CaseList"), }, { path: "planResult", name: "planResult", meta: { title: "计划运行结果", }, component: () => import("@/views/teprunner/plan/PlanResult"), children: [ { path: "caseResult", name: "caseResult", meta: { title: "用例运行结果", }, component: () => import("@/views/teprunner/plan/CaseResult"), }, ], }, ], }, 跟之前做增删改查不一样的地方是:**在父组件中,不会import子组件,而是通过LocalStorage进行组件之间传值。**因此这里增加了添加计划、编辑计划等路由。 ...
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 本文开发内容 pytest登场!本文将在Django中引入pytest,原理是先执行tep startproject命令创建pytest项目文件,然后从数据库中拉取代码写入文件,最后调用pytest命令运行用例。为了提高运行效率,用例运行是并行的,采用了多线程和多进程,两个都有,这在最后有个单独小结进行比较完整的说明。因为用例运行是异步的,所以前端并不知道什么时候执行完才能拿到运行结果,可以发多个HTTP请求轮询,但这种方式并不优雅,本文将采用WebSocket来实现用例结果查询。具体内容为: cases/<int:pk>/run运行用例接口 ws/teprunner/cases/<int:case_id>/result/用例结果查询接口 projects/<int:pk>/export下载项目环境接口 前端添加WebSocket请求 知识点涉及有点多:tep、pytest、同步、异步、多线程、多进程、回调函数、WebSocket、长连接、全双工、ASGI、WSGI、打ZIP包、文件字节流传输。 编写后端代码 编辑requirements.txt,添加tep和channels: tep==0.6.9 channels==3.0.3 tep是用来创建pytest项目的,channels是用来实现WebSocket的。 编辑teprunner/urls.py文件,添加HTTP路由: 首先实现run接口,新建teprunner/views/run.py文件: 这是运行用例的主体流程: 第一步从请求中获取用例id、运行环境、运行人,这里演示了获取user数据的两种方式:接口传参和从token中解析。然后根据project_id,run_env,user_id定义了pytest项目的路径。 第二步使用tep startproject创建项目文件,清空fixtures和tests目录,目的有两个:一是清掉tep默认fixtures和示例cases,防止对平台产生干扰;二是保证每次运行目录都是干净的,就不用单独去处理前端手动删掉fixture/case后,文件残留的问题。然后从数据库中拉取环境变量、fixtures等数据更新文件。 起多个线程,分别执行用例,执行前先拉取用例代码写入文件,这里是单条用例运行,之所以要用for循环,是因为用例迟早是要批量执行,在设计时就考虑到,避免后面走弯路。然后删掉数据库运行结果,通过subprocess起子进程调用pytest命令,最后在线程的回调函数中根据pytest_result保存用例结果到数据库中。 注意!run_case接口不会直接返回结果,前端是用WebSocket来查询结果的。 图中很多函数和类是我封装的,一个一个拆解来看: 这里定义了Django中存放pytest项目的目录文件,project_temp_name是按照project_id、env_name、user_id来划分的,目的是让运行目录尽量隔离开来,不要相互影响,借鉴了Docker容器的思想,可以把这个目录视为用例运行容器。继续: tep startproject命令创建pytest项目,pytest文件有特定组织方式,比如conftest.py文件等,tep提供了脚手架一条命令创建项目结构。继续: fixture_env_vars.py里面存放了tep的环境变量,Django这里每次都从数据库的env_var表中获取数据,动态更新到文件里面。setdefault是个骚操作,这行代码等价于: if env_name in mapping.keys(): mapping[env_name][name] = value else: mapping[env_name] = {name: value} 继续: 分别从数据库中获取代码写入fixture文件和case文件,把前端传参的运行环境写入conf文件。继续: 清空fixtures目录,清空tests目录。继续: pull_tep_files是写环境变量,写fixture文件,写conf文件三步的集合,复用代码。pull_case_files通过yield定义为了生成器,它和list的区别是不会一次把所有数据产生到内存中,而是每次用的时候产生一次,节约内存开销。delete_case_result用于运行用例前删除case_result表里面已经存在的这条用例的数据。case_result按照用例id和运行人存的多条,每个运行人都有一条属于自己的运行数据,避免数据相互干扰,返给前端的是运行时间最新的那一条! 继续: subprocess.getoutput()可以执行shell命令并返回执行结果,这里就拿到了pytest控制台日志,这个函数是在线程池中异步执行的,主线程不能一直等待它执行,所以需要有个回调函数,等它自己执行完了去调用这个回调函数。save_case_result就是个回调函数,它的入参pytest_result等于pytest_subprocess函数返回的元组,拆包后就能拿到outout、cmd、case_id、run_env、run_user_nickname,从中解析出result和elapsed后,就可以存库了,无则新增,有则更新。 run接口做好了,再接口做下载环境接口,编辑teprunner/views/project.py: 打包的代码是从网上找的,把source_dir打包成zip_filename文件。继续: file_iterator函数也是网上找的,把二进制文件读取为字节流,传输给前端,需要使用StreamingHttpResponse对象并添加Content-Type和Content-Disposition。红框的代码跟run接口类似,区别在于目录换成了export_temp_dir(),且不包含测试用例,生成zip文件后会把导出临时目录删掉,防止冲突。 两个HTTP接口做完了,开始实现WebSocket。WSGI一种网关接口,是Python为了解决Web服务器端与客户端之间的通信问题而产生的,不支持WebSocket;ASGI是WSGI的扩展,意思是异步网关接口,支持WebSocket。编辑teprunnerbackend/urls.py文件: 添加了WebSocket路由。编辑teprunnerbackend/asgi.py文件: 添加websocket的URLRouter,http保持默认。编辑teprunnerbackend/settings.py文件: INSTALLED_APPS中添加channels,继续: 添加ASGI应用配置和CHANNEL配置。CHANNEL_LAYERS是一种通信系统,允许多个Consumer实例之间互相通信,以及与外部Django程序实现互通。学习版这里使用的InMemory。 生产中不建议使用InMemory,可能会有性能问题,而是应该使用Redis: CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [("127.0.0.1", 6379)], }, }, } 最后,编辑teprunner/views/case.py文件: ...
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 本文开发内容 用例管理是对用例进行增删改查,按照前面文章的思路,把它做出来应该不难,如果你已经自己写好了,那么可以和本文提交的代码比较下看看。除了增删改查,用例管理还需要提供运行用例的入口,在操作列添加一个运行按钮,单条用例运行,并弹窗展示运行结果。用例列表需要能看到每条用例执行情况,添加表格列用于展示,其中“运行结果”列要有超链接,点击查看上次运行结果。为了避免修改别人用例出错,还需要有个复制用例功能。除了在线编辑,平台应支持下载项目环境到本地,无缝切换到PyCharm,让新用户快速上手。综上所述,本文开发内容如下: 用例增删改查。 运行按钮,点击后弹出“运行用例结果”。 添加表格展示列,其中“运行结果”用颜色区分不同结果状态:passed,failed,error,点击超链接后弹出“查看用例结果”。 复制按钮,点击后生成一条复制的新用例。 新增/编辑页面,添加运行按钮。 运行/查看页面,添加编辑按钮。 下载环境到本地。 工作量主要在前端,后端先做一部分。 编写后端代码 编辑teprunner/urls.py,添加路由: 除了增删改查,还添加了2个接口,一个用于获取用例运行的结果,一个用于复制测试用例。 编辑teprunner/models.py,添加模型: 第一个Model是Case,没有定义name字段,实际上用例ID和用例描述已经够用了,name不知道填什么,有点多余。第二个Model是CaseResult,会记录运行结果,这个运行结果是从pytest执行日志最后一行的Summary截取出来的,比如1 passed,1 failed,耗时也是从Summary截取出来的。output存放代码运行后打到控制台的输出日志。最后三个字段记录运行环境、运行人和运行时间,运行时间取的数据库记录被添加/更新的时间。添加好以后迁移到数据库中: python manage.py makemigrations python manage.py migrate 编辑teprunner/serializers.py,添加序列化器,第一个序列化器是CaseSerializer: 只关联了Case模型的字段。 第二个序列化器是CaseListSerializer: 它主要用来返回用例列表的数据,除了用例本身,还需要用例结果。serializers.SerializerMethodField用于自定义序列化字段,定义好字段后,在下面添加get_前缀+字段名的方法,这是Django的规则,然后在方法内部添加取值代码,比如get_result(),instance是指Meta.model定义的模型,这里就等同于Case,通过instance.id获取到Case.id并赋值给case_id变量,从CaseResult的对象中过滤case_id匹配的用例结果,按运行时间倒序,取最新1条结果的result字段的值。其他字段的处理是类似的,runTime这里做了下日期格式化: 第三个序列化器是CaseResultSerializer: 只关联了CaseResult模型的字段,runTime同样做了日期格式化。从这里也能更清晰的看到,instance就是指的自己! 新建teprunner/views/case.py文件,添加视图,先把简单的两个视图做了,第一个视图是case_result: 这里是个知识点,采用@api_view注解的函数视图,如果想用序列化器,那么采用这种方式。 第二个视图是copy_case: 根据case_id查找到现有Case对象,在case.desc后面添加--复制后缀,其他字段数据复用,保存,就完成复制用例了。 第三个视图是CaseViewSet: 继承了ModelViewSet,重写了list列表查询,因为如果不重写,默认返回的结果是serializer_class = CaseSerializer序列化器的内容。我们需要的是CaseListSerializer这个序列化器的内容。这里也是一个知识点!如果想在类视图中,使用serializer_class以外的序列化器加分页,采用这种方式。 重写的list方法首先添加了project_id、case_id、desc、api四个过滤条件,与前端的当前项目、用例ID搜索框、用例描述搜索框、API路径搜索框一一对应。__icontains表示包含,相当于模糊匹配,i忽略大小写。接着用到了自定义分页的类,按照统一的分页格式,返回序列化器的数据。 最后,重写了update方法,用现有的创建人进行更新。 编写前端代码 编辑router/index.js,添加路由: 编辑views/teprunner/index.vue,添加菜单项: 新建views/teprunner/case/CaseManagement.vue文件,增删改查基础代码不再另加赘述,挑选新代码: 在运行结果列,添加了超链接,点击后打开用例结果弹窗,这个弹窗查看和运行用例都会用到,用第一个参数type做区分: getResultColor方法返回颜色样式: 由于用例结果弹窗和用例列表表格都会有这个字段,于是把resultColor方法提取到了utils/commonMethods.js文件中: CaseManagement.vue是一个父组件,它有3个子组件,如下图所示: 通过import后,添加标签代码引入: 是否显示弹窗通过DialogFormVisible的true/false来控制,DialogTitle指定弹窗标题。:开头是父组件向子组件传值, @开头是接受子组件发过来的emit事件通知,从而调用父组件中方法。这样就把这几个组件绑定在一起了! 从这里开始,这几个组件之间的关系会稍微有点绕,读不懂的话多结合源码看看! ...
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 本文开发内容 现在正式进入测试相关功能开发。teprunner测试平台底层是pytest,中间层是tep,还没了解的朋友可以先看看tep的文章,整个平台的设计思路和后面用例的执行都会基于这个工具。tep的测试用例是放在.py文件里面的,全局变量或者说环境变量是引用的env_vars,公共函数和复用接口是引用的fixtures,在做成平台后,需要把这两个部分独立为两个功能模块。多个项目的接口自动化数据需要隔离开来,要有个项目管理功能。本文将开发四个用例前置模块: 后台管理–项目管理 接口自动化–语法说明 接口自动化–环境变量 接口自动化–fixtures 语法说明给出了环境变量、fixtures、用例示例(单个接口、多个接口–增删改查)、请求方法(get、post、put、delete)的使用方法。 编写后端代码 先创建名为teprunner的app: django-admin startapp teprunner 在settings的INSTALLED_APPS中添加配置让app生效: 并在urls.py添加路由: 编辑user/fixtures/user.json,添加一个接口自动化的菜单: 截图只截了管理员,测试和开发角色,也需要添加上。access均为true,这三个角色都可以访问。添加后把数据同步到数据库: python manage.py loaddata user 编辑teprunner/models.py,新增模型: 包括了项目、环境变量和fixture三张表。Project.env_config用于给项目配置多个环境。不同项目不同环境的环境变量不一样,在EnvVar中添加了project_id和env_name,并按(“project_id”, “env_name”, “name”)作为唯一键,不允许重复。不同项目的fixtures不同,不关心环境,在Fixture中只添加了project_id。 Fixture.code用于存放代码,字段类型为models.TextField,容纳更多字符。 models编写好后,迁移到数据库: python manage.py makemigrations python manage.py migrate 新建teprunner/serializers.py文件,新增序列化器: 数据库字段的命名是用的下划线,接口返回前端和前端传参是用的驼峰,所以这里通过source这种方式给env_config、project_id、env_name、creator_nickname进行了序列化器的重命名。为了前端更方便处理,使用serializers.CharField把int转化为了str,由于请求时id非必须,增加参数required=False。 teprunner会有很多视图,写在一个文件里面有点臃肿,这里创建views文件夹,新增views/project.py项目视图: Django REST framework的ModelViewSet视图类能节省很多代码,只需要三行就把项目增删改查接口做了。project_env是函数视图,请求方法为GET,它的作用是返回项目环境列表,当前项目和当前环境,默认为第一个项目和第一个环境。 前端需要切换不同项目和不同环境,下拉框数据来源于这个接口。 新建views/envvar.py新增环境变量视图: 环境变量跟项目和环境是强关联的,需要根据项目环境进行过滤,所以重写了list方法,从前端请求拿到curProjectId和curEnvName,通过EnvVar.objects.filter进行过滤。from django.db.models import Q支持多种过滤条件,这在后面的代码中还能看到,比如模糊匹配。 新建views/fixture.py新增fixture视图: fixture跟项目是强关联的,需要根据项目进行过滤,所以重写了list方法,从前端请求拿到curProjectId,通过Fixture.objects.filter进行过滤。Fixture有个creatorNickname字段,新增fixture时使用的是当前登录用户的昵称,修改fixture时需要用已保存的创建者,重写update方法来实现这个处理。 最后,新建teprunner/urls.py文件,把视图添加到路由中: 编写前端代码 编辑package.json,添加依赖: fixtures是代码形式的,这是测试平台第一次使用代码编辑组件。添加依赖后记得执行npm install进行安装。 编辑router/index.js: 添加接口自动化的路由,并把home重定向改为语法说明路由。children表示子路由。path是路由地址。meta.title用于显示菜单名。 编辑views/console/index.vue: 添加项目管理菜单。新建ProjectManagement.vue和AddProject.vue,添加项目管理增删改查代码,跟用户管理类似,不再另加赘述。值得注意的是环境配置placeholder="请输入环境英文名(多个环境英文逗号分隔)",因为tep.env_vars.mapping会用到环境名作为key,所以只能用英文名! 编辑components/WrapComponent.vue,添加一个插槽: ...
本文是一篇过渡,在进行用例管理模块开发之前,有必要把入门篇开发完成的代码部署到Linux系统Docker中,把部署流程走一遍,这个过程对后端设计有决定性影响。
本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 前言 2021年,测试平台如雨后春笋般冒了出来,我就是其中一员,写了一款pytest内核测试平台,在公司落地。分享出来后,有同学觉得挺不错,希望能开源,本着“公司代码不要传到网上去,以免引起不必要麻烦”的原则,只能在家从头写一个,边重新梳理代码边温习巩固知识点,以学习交流为目的,定义为“学习版”。 功能展示 登录 接口自动化 接口自动化–Dashboard: 接口自动化–环境变量: 接口自动化–fixtures: 接口自动化–用例管理: 接口自动化–用例管理–编辑用例: 接口自动化–测试计划: 接口自动化–语法说明: 小工具 小工具–共享脚本: 小工具–HTTP状态查询: 后台管理 后台管理–用户管理 后台管理–项目管理 本文开发内容 登录,登出 首页,修改密码,个人信息 后台管理,用户管理 JWT认证 本文先打个基础,既是测试平台基本结构,也可以作为CMS基础框架,定制开发各种小型项目。 技术栈 Node.js 12.16.3 Vue 4.5.11 Python 3.8 Django 3.1.3 Django REST framework 3.12.2 SQLite 3 IDE编辑器推荐PyCharm旗舰版,既能写Django也能写Vue项目。数据库使用Django自带SQLite ,省去安装MySQL和Navicat/Workbench麻烦,轻量级开发。SQLiteStudio为SQLite数据库可视化工具,只需要下载即可,无需安装,解压就用:: https://sqlitestudio.pl/ 由于会用到models.JSONField,SQLite默认不兼容,所以需要下载sqlite3.dll文件替换下: https://www.sqlite.org/download.html 根据Python版本选择,比如我的windows安装的Python38-32,下载了sqlite-dll-win32-x86-3340100.zip这个软件包,解压后将D:\Program Files (x86)\Python38-32\DLLs\sqlite3.dll替换。 创建Vue项目 设置npm淘宝镜像: npm config set registry https://registry.npm.taobao.org 安装Vue CLI: npm install -g @vue/cli 创建teprunner-frontend项目: ...