<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Teprunner on 测试开发刚哥</title>
    <link>https://dongfanger.github.io/tags/teprunner/</link>
    <description>Recent content in Teprunner on 测试开发刚哥</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Sun, 11 Jan 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://dongfanger.github.io/tags/teprunner/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>teprunner测试平台入门级部署手册发布啦</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000011-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%85%A5%E9%97%A8%E7%BA%A7%E9%83%A8%E7%BD%B2%E6%89%8B%E5%86%8C%E5%8F%91%E5%B8%83%E5%95%A6/</link>
      <pubDate>Sun, 11 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000011-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%85%A5%E9%97%A8%E7%BA%A7%E9%83%A8%E7%BD%B2%E6%89%8B%E5%86%8C%E5%8F%91%E5%B8%83%E5%95%A6/</guid>
      <description>很多朋友是因为teprunner，也就是这个小众的pytest内核测试平台关注的公众号。为了让大家更好的上手teprunner，我更新了它的README，希望能让小伙伴们根据这些文档内容，一步一步的在自己本地电脑上把项目跑起来。项目跑起来之后，就可以参考前面一系列的学习教程，自己动手做一遍，在做的过程中和teprunner进行对比，不懂的点逐一突破，由点到面，完整实现。**这种学习方式能更快速的掌</description>
    </item>
    <item>
      <title>teprunner重磅更新Git打通PyCharm与测试平台</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000010-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E9%87%8D%E7%A3%85%E6%9B%B4%E6%96%B0git%E6%89%93%E9%80%9Apycharm%E4%B8%8E%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0/</link>
      <pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000010-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E9%87%8D%E7%A3%85%E6%9B%B4%E6%96%B0git%E6%89%93%E9%80%9Apycharm%E4%B8%8E%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0/</guid>
      <description>经过Python测试交流群的小伙伴群策群力，teprunner添加了一个重要功能，把PyCharm中的代码，通过Git同步到测试平台中，生成测试用例。这样，teprunner就成了一个名副其实的&lt;strong&gt;pytest脚本在线管理平台&lt;/strong&gt;。</description>
    </item>
    <item>
      <title>teprunner测试平台定时任务这次终于稳了</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000009-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E8%BF%99%E6%AC%A1%E7%BB%88%E4%BA%8E%E7%A8%B3%E4%BA%86/</link>
      <pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000009-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E8%BF%99%E6%AC%A1%E7%BB%88%E4%BA%8E%E7%A8%B3%E4%BA%86/</guid>
      <description>teprunner测试平台已经有一个多月没有更新了，主要原因是定时任务不够稳定，经过反复试错，找到了解决办法，这次终于稳定了。</description>
    </item>
    <item>
      <title>teprunner测试平台用例编写等体验响应式升级</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000008-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E7%94%A8%E4%BE%8B%E7%BC%96%E5%86%99%E7%AD%89%E4%BD%93%E9%AA%8C%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8D%87%E7%BA%A7/</link>
      <pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000008-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E7%94%A8%E4%BE%8B%E7%BC%96%E5%86%99%E7%AD%89%E4%BD%93%E9%AA%8C%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8D%87%E7%BA%A7/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;本文开发内容&#34;&gt;本文开发内容&lt;/h1&gt;
&lt;p&gt;在免费开源的测试平台中，teprunner的UI界面应该算得上第一梯度了，BUT，美中不足，在不同分辨率显示器（比如13.3寸小屏笔记本）下布局大的弹出窗口，表现不尽如人意。而这又是核心功能代码编辑的界面，比如用例编写、fixture编写，很有必要优化升级。除此之外还有滚动条的问题：竖向双滚动条和横向滚动条，也对体验造成影响。本文将对这些内容进行改造，具体如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编写用例页面，从弹窗改为子页面。&lt;/li&gt;
&lt;li&gt;编写fixture页面，从弹窗改为子页面。&lt;/li&gt;
&lt;li&gt;代码编辑区域高度自适应。&lt;/li&gt;
&lt;li&gt;缩小左侧菜单，移动项目环境和新增按钮同一行，&lt;strong&gt;扩大编辑区面积。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;消除所有双向滚动条。&lt;/li&gt;
&lt;li&gt;消除所有横向滚动条。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;语法说明添加环境变量和fixtures示例代码。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;其他细节优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;响应式是指同一套前端代码在不同浏览器（PC端、移动端等）都能适配，&lt;strong&gt;Element UI已经是响应式设计了，本文的响应式体现在代码编辑区行高根据window.innerHeight自动计算设定。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;升级效果展示&#34;&gt;升级效果展示&lt;/h1&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/abb289b0c2ad40d0b876edfd0078712a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;左侧菜单栏变窄；添加环境变量和fixtures示例代码。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8f2785c1c9bd4d5ead1ab11ab700afe6.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;项目环境和新增按钮同一行显示，扩大有效内容可视面积。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/21e3c8c2725b414d841f5aa8166eb133.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;fixtures的新增和编辑从弹窗改为子路由页面，代码编辑区高度自适应，纵向滚动条只出现一个。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/978e0307dd804de58c101d3f8d908fe0.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;消除横向滚动条；缩小了用例描述列以外其他列宽；替换操作按钮文字为图标。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/a0ddb7fee2e74834b416755ead80e364.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;用例的新增和编辑从弹窗改为子路由页面，代码编辑区高度自适应，纵向滚动条只出现一个。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/98b3d62d1d144be595d485264f5f0a11.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;用例运行结果的新增和编辑从弹窗改为子路由页面，代码编辑区高度自适应，纵向滚动条只出现一个。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ac3e7fdec3304676a6c32a6815e55d8b.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;测试计划的新增，编辑，运行结果也做了相同调整。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;整体而言，有效信息的可视面积和可操作面积都尽可能扩大了！&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;关键技术点&#34;&gt;关键技术点&lt;/h1&gt;
&lt;p&gt;前端代码修改文件如下：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4181f693f5a74f7d8e3b474ffc40e44a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;比较繁琐，&lt;strong&gt;没有新知识&lt;/strong&gt;，这里就不一一讲解了，而是选择用到的关键技术点来进行&lt;strong&gt;知识回顾&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;template添加&lt;code&gt;&amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;主页面div添加&lt;code&gt;v-if=&amp;quot;$route.name === &#39;name&#39;&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在router/index.js中添加children子路由&lt;/li&gt;
&lt;li&gt;跳转页面前，存localStorage&lt;/li&gt;
&lt;li&gt;新页面create()时，取localStorage&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this.$router.push&lt;/code&gt;跳转路由&lt;/li&gt;
&lt;li&gt;div同行显示用&lt;code&gt;float: left&lt;/code&gt;，紧跟的div换行用&lt;code&gt;clear: both&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;window.innerHeight获取屏幕高度&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;小结&#34;&gt;小结&lt;/h1&gt;
&lt;p&gt;本文完成了对teprunner测试平台前端UI界面的体验升级，&lt;strong&gt;已经适配13.3寸到27寸显示器&lt;/strong&gt;，如果还在担心不好用的话，那么现在可以好好试试了。定时任务从目前优化效果来看，已经趋于稳定，不久就会和大家见面。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料：&lt;/p&gt;
&lt;p&gt;Element UI图标按钮样式 &lt;a href=&#34;https://www.jianshu.com/p/a81cd965943c&#34;&gt;https://www.jianshu.com/p/a81cd965943c&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>teprunner测试平台测试计划批量运行用例</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000007-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E6%B5%8B%E8%AF%95%E8%AE%A1%E5%88%92%E6%89%B9%E9%87%8F%E8%BF%90%E8%A1%8C%E7%94%A8%E4%BE%8B/</link>
      <pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000007-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E6%B5%8B%E8%AF%95%E8%AE%A1%E5%88%92%E6%89%B9%E9%87%8F%E8%BF%90%E8%A1%8C%E7%94%A8%E4%BE%8B/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;本文开发内容&#34;&gt;本文开发内容&lt;/h1&gt;
&lt;p&gt;上一篇文章已经把pytest引入到测试平台中，通过多线程和多进程的方式，运行测试用例。有了这个基础，做批量运行用例的功能就很简单了，只需要前端传入一个CaseList即可。本文的后端代码是增删改查和复用run_case相关代码做个run_plan。前端代码将学习如何通过LocalStorage在非父子组件之间传递数据。具体开发内容如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;测试计划增删改查&lt;/li&gt;
&lt;li&gt;测试计划动态添加移除测试用例&lt;/li&gt;
&lt;li&gt;运行测试计划，批量运行用例&lt;/li&gt;
&lt;li&gt;计划运行结果、用例运行结果&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;编写后端代码&#34;&gt;编写后端代码&lt;/h1&gt;
&lt;p&gt;编辑teprunner/urls.py文件，添加测试计划的路由：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/1c96969043d24bd0b87c84d657795fa5.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;编辑teprunner/models.py文件，添加3张表的model：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/fcd47aad64ed43e4a1d72f29dfa16568.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Plan存放测试计划，PlanCase存放计划和用例关联关系，PlanResult和CaseResult类似，这里之所以把单独出来一张结果表，是为了和用例结果CaseResult相互独立，防止计划结果覆盖用例结果，毕竟CaseResult一般是手工跑出来的，PlanResult一般是定时任务跑出来的，功能数据解耦后处理起来也更方便。&lt;/p&gt;
&lt;p&gt;编辑teprunner/serializers.py文件，添加序列化器：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/b2608ab6d83845e2a7c26f365a6ee6e3.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/49b760af5b804974a9d5d8aa06a87402.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5992c4f23daf478f9984ca7186da9881.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;为了前端展示需要，这里额外关联了一些字段进行展示。尤其是：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/97d5f6ac98354ce5b2d1a501f45f3d94.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/1fdbb3cd5aef45e89a27b0fc3d3cb3ed.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/9fd4fcda762342339a84b37707c177be.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;caseNum用例总条数，passedNum成功条数，failedNum失败条数，errorNum错误条数。成功定义为：只有passed，没有failed和error的用例。失败定义为：只有failed，没有error的用例。错误定义为：只要有error的用例。&lt;/p&gt;
&lt;p&gt;本文还展示了，&lt;strong&gt;如果序列化器的某个字段只在存库时使用，返回响应时不展示，该怎么处理&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ebdfaf64af22487994b1f0bf89b93140.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;重写to_representation方法，把不需要返回的字段pop移出去。这里output在计划运行结果不需要，在用例运行结果才需要，为了减少传输数据，就在计划运行结果的接口响应中移出去了。&lt;/p&gt;
&lt;p&gt;最后来写View，新增teprunner/views/plan.py文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4353e6c7d27c4744bee01d379a0ffe9a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;重写list方法，添加查询条件。重写destroy方法，判断必须先删除关联测试用例，才能删除测试计划。&lt;/p&gt;
&lt;p&gt;PlanCaseView类似：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/e501d7506ae84627800008bd846012cf.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加add方法：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/486959963d0c41ce976ec387550e1748.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;在添加用例时写入plan_case表。添加remove方法：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d26ec36f6aea4ff4aee737fc4165d453.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;在移除用例时从plan_case表删除数据。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/55b6f7f5f39642fa9cb85bf810f7d579.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;result视图用来返回计划运行结果，根据searchType来分别查询用例总条数、成功条数、失败条数、错误条数。&lt;/p&gt;
&lt;p&gt;继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5b57a7743f314605bfe41c7b1667a75e.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;plan.case_result和case.case_result类似，返回测试计划的每条用例的运行结果。&lt;/p&gt;
&lt;p&gt;在测试计划添加用例的时候，有个用例列表，需要增加2个过滤条件，编辑teprunner/views/case.py文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ab6d81b288cf41929ebb13657ff87609.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;第一个过滤条件是排除当前计划已经添加过的用例：exclude_plan_id由前端传入，根据PlanCase关联关系找到已添加的用例进行过滤；第二个查询条件是关键字：用例ID或用例描述，跟PlanCaseView.list()里面一样。&lt;/p&gt;
&lt;p&gt;最后，编辑teprunner/views/run.py文件，增加批量运行用例的功能：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/84b451fcbfb5456d896dcf14c8e279e9.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加delete_plan_result方法，在运行用例前，先把已有结果删掉，注意这里的model是PlanResult。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/a5ecc1634f3b4434887225e16c606fb1.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;修改pytest_subprocess函数，添加plan_id字段。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/9732bd147aa9445eb23ae6af75c02f7d.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;修改save_case_result函数，判断没有plan_id就存case_result表，有plan_id就存plan_result表。&lt;/p&gt;
&lt;p&gt;复制run_case的代码为run_plan：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/c97b32984c5044e798ed7c68e26c1951.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;修改case相关代码为plan。值得注意的是中间那段代码，根据plan_id从PlanCase中找到关联用例，生成了case_list，这样就复用run_case的多线程和多进程代码，批量运行测试用例了。&lt;/p&gt;
&lt;h1 id=&#34;编写前端代码&#34;&gt;编写前端代码&lt;/h1&gt;
&lt;p&gt;编辑src/router/index.js文件，添加测试计划相关路由：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;		{
            path: &amp;#34;plan&amp;#34;,
            name: &amp;#34;plan&amp;#34;,
            meta: {
              title: &amp;#34;测试计划&amp;#34;,
            },
            component: () =&amp;gt; import(&amp;#34;@/views/teprunner/plan/PlanManagement.vue&amp;#34;),
            children: [
              {
                path: &amp;#34;addPlan&amp;#34;,
                name: &amp;#34;addPlan&amp;#34;,
                meta: {
                  title: &amp;#34;添加计划&amp;#34;,
                },
                component: () =&amp;gt; import(&amp;#34;@/views/teprunner/plan/PlanEditor&amp;#34;),
              },
              {
                path: &amp;#34;editPlan&amp;#34;,
                name: &amp;#34;editPlan&amp;#34;,
                meta: {
                  title: &amp;#34;编辑计划&amp;#34;,
                },
                component: () =&amp;gt; import(&amp;#34;@/views/teprunner/plan/PlanEditor&amp;#34;),
              },
              {
                path: &amp;#34;caseList&amp;#34;,
                name: &amp;#34;caseList&amp;#34;,
                meta: {
                  title: &amp;#34;用例列表&amp;#34;,
                },
                component: () =&amp;gt; import(&amp;#34;@/views/teprunner/plan/CaseList&amp;#34;),
              },
              {
                path: &amp;#34;planResult&amp;#34;,
                name: &amp;#34;planResult&amp;#34;,
                meta: {
                  title: &amp;#34;计划运行结果&amp;#34;,
                },
                component: () =&amp;gt; import(&amp;#34;@/views/teprunner/plan/PlanResult&amp;#34;),
                children: [
                  {
                    path: &amp;#34;caseResult&amp;#34;,
                    name: &amp;#34;caseResult&amp;#34;,
                    meta: {
                      title: &amp;#34;用例运行结果&amp;#34;,
                    },
                    component: () =&amp;gt; import(&amp;#34;@/views/teprunner/plan/CaseResult&amp;#34;),
                  },
                ],
              },
            ],
          },
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;跟之前做增删改查不一样的地方是：**在父组件中，不会import子组件，而是通过LocalStorage进行组件之间传值。**因此这里增加了添加计划、编辑计划等路由。&lt;/p&gt;</description>
    </item>
    <item>
      <title>teprunner测试平台Django引入pytest完整源码</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000006-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0django%E5%BC%95%E5%85%A5pytest%E5%AE%8C%E6%95%B4%E6%BA%90%E7%A0%81/</link>
      <pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000006-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0django%E5%BC%95%E5%85%A5pytest%E5%AE%8C%E6%95%B4%E6%BA%90%E7%A0%81/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;本文开发内容&#34;&gt;本文开发内容&lt;/h1&gt;
&lt;p&gt;pytest登场！本文将在Django中引入pytest，原理是先执行tep startproject命令创建pytest项目文件，然后从数据库中拉取代码写入文件，最后调用pytest命令运行用例。为了提高运行效率，用例运行是并行的，采用了多线程和多进程，两个都有，这在最后有个单独小结进行比较完整的说明。因为用例运行是异步的，所以前端并不知道什么时候执行完才能拿到运行结果，可以发多个HTTP请求轮询，但这种方式并不优雅，本文将采用WebSocket来实现用例结果查询。具体内容为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cases/&amp;lt;int:pk&amp;gt;/run&lt;/code&gt;运行用例接口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ws/teprunner/cases/&amp;lt;int:case_id&amp;gt;/result/&lt;/code&gt;用例结果查询接口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;projects/&amp;lt;int:pk&amp;gt;/export&lt;/code&gt;下载项目环境接口&lt;/li&gt;
&lt;li&gt;前端添加WebSocket请求&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;知识点涉及有点多：tep、pytest、同步、异步、多线程、多进程、回调函数、WebSocket、长连接、全双工、ASGI、WSGI、打ZIP包、文件字节流传输。&lt;/p&gt;
&lt;h1 id=&#34;编写后端代码&#34;&gt;编写后端代码&lt;/h1&gt;
&lt;p&gt;编辑requirements.txt，添加tep和channels：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;tep==0.6.9
channels==3.0.3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;tep是用来创建pytest项目的，channels是用来实现WebSocket的。&lt;/p&gt;
&lt;p&gt;编辑teprunner/urls.py文件，添加HTTP路由：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/71e7cccd40254bb886c396048dadf6af.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;首先实现run接口，新建teprunner/views/run.py文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/1482f5173eb94926b01b3ad07a6385d0.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这是运行用例的主体流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一步从请求中获取用例id、运行环境、运行人，这里演示了获取user数据的两种方式：接口传参和从token中解析。然后根据project_id，run_env，user_id定义了pytest项目的路径。&lt;/li&gt;
&lt;li&gt;第二步使用tep startproject创建项目文件，清空fixtures和tests目录，目的有两个：一是清掉tep默认fixtures和示例cases，防止对平台产生干扰；二是保证每次运行目录都是干净的，就不用单独去处理前端手动删掉fixture/case后，文件残留的问题。然后从数据库中拉取环境变量、fixtures等数据更新文件。&lt;/li&gt;
&lt;li&gt;起多个线程，分别执行用例，执行前先拉取用例代码写入文件，这里是单条用例运行，之所以要用for循环，是因为用例迟早是要批量执行，在设计时就考虑到，避免后面走弯路。然后删掉数据库运行结果，通过subprocess起子进程调用pytest命令，最后在线程的回调函数中根据pytest_result保存用例结果到数据库中。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;注意！run_case接口不会直接返回结果，前端是用WebSocket来查询结果的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;图中很多函数和类是我封装的，一个一个拆解来看：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/87c46d8175da4b6bb0a65934443cfc15.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这里定义了Django中存放pytest项目的目录文件，project_temp_name是按照project_id、env_name、user_id来划分的，目的是让运行目录尽量隔离开来，不要相互影响，借鉴了Docker容器的思想，可以把这个目录视为用例运行容器。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/722bf303d1954b17acf44d5ffec77b9f.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;tep startproject命令创建pytest项目，pytest文件有特定组织方式，比如conftest.py文件等，tep提供了脚手架一条命令创建项目结构。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/2d67ae2147504315972dea092501d3b0.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;fixture_env_vars.py里面存放了tep的环境变量，Django这里每次都从数据库的env_var表中获取数据，动态更新到文件里面。setdefault是个骚操作，这行代码等价于：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;if&lt;/span&gt; env_name &lt;span style=&#34;color:#0550ae&#34;&gt;in&lt;/span&gt; mapping&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;keys&lt;span style=&#34;color:#1f2328&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mapping&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;env_name&lt;span style=&#34;color:#1f2328&#34;&gt;][&lt;/span&gt;name&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;else&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    mapping&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;env_name&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;name&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; value&lt;span style=&#34;color:#1f2328&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/f8475fdd82504e2d91977a43571c2eca.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;分别从数据库中获取代码写入fixture文件和case文件，把前端传参的运行环境写入conf文件。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/dd57e6fa2537457783ff455741aadd16.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;清空fixtures目录，清空tests目录。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/efcf49e2264f4fc7adcd548d1a684337.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;pull_tep_files是写环境变量，写fixture文件，写conf文件三步的集合，复用代码。pull_case_files通过yield定义为了生成器，它和list的区别是不会一次把所有数据产生到内存中，而是每次用的时候产生一次，节约内存开销。delete_case_result用于运行用例前删除case_result表里面已经存在的这条用例的数据。case_result按照用例id和运行人存的多条，每个运行人都有一条属于自己的运行数据，避免数据相互干扰，返给前端的是运行时间最新的那一条！&lt;/p&gt;
&lt;p&gt;继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/980539c2eed4477593219cebec3796da.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;subprocess.getoutput()可以执行shell命令并返回执行结果，这里就拿到了pytest控制台日志，这个函数是在线程池中异步执行的，主线程不能一直等待它执行，所以需要有个回调函数，等它自己执行完了去调用这个回调函数。save_case_result就是个回调函数，它的入参pytest_result等于pytest_subprocess函数返回的元组，拆包后就能拿到outout、cmd、case_id、run_env、run_user_nickname，从中解析出result和elapsed后，就可以存库了，无则新增，有则更新。&lt;/p&gt;
&lt;p&gt;run接口做好了，再接口做下载环境接口，编辑teprunner/views/project.py：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/72bf0e5f7e674d8482921741ebb8f928.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;打包的代码是从网上找的，把source_dir打包成zip_filename文件。继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/a50944e18d2345b88e7ccb728e47af84.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;file_iterator函数也是网上找的，把二进制文件读取为字节流，传输给前端，需要使用StreamingHttpResponse对象并添加&lt;code&gt;Content-Type&lt;/code&gt;和&lt;code&gt;Content-Disposition&lt;/code&gt;。红框的代码跟run接口类似，区别在于目录换成了export_temp_dir()，且不包含测试用例，生成zip文件后会把导出临时目录删掉，防止冲突。&lt;/p&gt;
&lt;p&gt;两个HTTP接口做完了，开始实现WebSocket。WSGI一种网关接口，是Python为了解决Web服务器端与客户端之间的通信问题而产生的，不支持WebSocket；ASGI是WSGI的扩展，意思是异步网关接口，支持WebSocket。编辑teprunnerbackend/urls.py文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ec702894023e4a068e14f158dbffe5c0.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加了WebSocket路由。编辑teprunnerbackend/asgi.py文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/48a80d492a1c42aeb7f9caed12be48dc.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加websocket的URLRouter，http保持默认。编辑teprunnerbackend/settings.py文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/84a0a9dcae3346829fa45e4ae0e60869.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;INSTALLED_APPS中添加channels，继续：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/b9e11ff4a40d467b901fb8ddedc6e89f.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加ASGI应用配置和CHANNEL配置。CHANNEL_LAYERS是一种通信系统，允许多个Consumer实例之间互相通信，以及与外部Django程序实现互通。学习版这里使用的InMemory。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生产中不建议使用InMemory，可能会有性能问题，而是应该使用Redis：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CHANNEL_LAYERS &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;BACKEND&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;channels_redis.core.RedisChannelLayer&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;CONFIG&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;hosts&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;[(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;6379&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#1f2328&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#1f2328&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;最后，编辑teprunner/views/case.py文件：&lt;/p&gt;</description>
    </item>
    <item>
      <title>teprunner测试平台开发用例管理不只有增删改查</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000005-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E7%94%A8%E4%BE%8B%E7%AE%A1%E7%90%86%E4%B8%8D%E5%8F%AA%E6%9C%89%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000005-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E7%94%A8%E4%BE%8B%E7%AE%A1%E7%90%86%E4%B8%8D%E5%8F%AA%E6%9C%89%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;本文开发内容&#34;&gt;本文开发内容&lt;/h1&gt;
&lt;p&gt;用例管理是对用例进行增删改查，按照前面文章的思路，把它做出来应该不难，如果你已经自己写好了，那么可以和本文提交的代码比较下看看。除了增删改查，用例管理还需要提供运行用例的入口，在操作列添加一个运行按钮，单条用例运行，并弹窗展示运行结果。用例列表需要能看到每条用例执行情况，添加表格列用于展示，其中“运行结果”列要有超链接，点击查看上次运行结果。为了避免修改别人用例出错，还需要有个复制用例功能。除了在线编辑，平台应支持下载项目环境到本地，无缝切换到PyCharm，让新用户快速上手。综上所述，本文开发内容如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用例增删改查。&lt;/li&gt;
&lt;li&gt;运行按钮，点击后弹出“运行用例结果”。&lt;/li&gt;
&lt;li&gt;添加表格展示列，其中“运行结果”用颜色区分不同结果状态：passed，failed，error，点击超链接后弹出“查看用例结果”。&lt;/li&gt;
&lt;li&gt;复制按钮，点击后生成一条复制的新用例。&lt;/li&gt;
&lt;li&gt;新增/编辑页面，添加运行按钮。&lt;/li&gt;
&lt;li&gt;运行/查看页面，添加编辑按钮。&lt;/li&gt;
&lt;li&gt;下载环境到本地。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;工作量主要在前端，后端先做一部分。&lt;/p&gt;
&lt;h1 id=&#34;编写后端代码&#34;&gt;编写后端代码&lt;/h1&gt;
&lt;p&gt;编辑teprunner/urls.py，添加路由：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/212bda04278c4eea864f64edbe8e6c2b.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;除了增删改查，还添加了2个接口，一个用于获取用例运行的结果，一个用于复制测试用例。&lt;/p&gt;
&lt;p&gt;编辑teprunner/models.py，添加模型：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ebbca98ae0734477b8b8ee8a2b2713bd.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;第一个Model是Case，没有定义name字段，实际上用例ID和用例描述已经够用了，name不知道填什么，有点多余。第二个Model是CaseResult，会记录运行结果，这个运行结果是从pytest执行日志最后一行的Summary截取出来的，比如1 passed，1 failed，耗时也是从Summary截取出来的。output存放代码运行后打到控制台的输出日志。最后三个字段记录运行环境、运行人和运行时间，运行时间取的数据库记录被添加/更新的时间。添加好以后迁移到数据库中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py makemigrations
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编辑teprunner/serializers.py，添加序列化器，第一个序列化器是CaseSerializer：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/fd7c0235d6684a7aae21827c39a2af96.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;只关联了Case模型的字段。&lt;/p&gt;
&lt;p&gt;第二个序列化器是CaseListSerializer：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/096c709c347f463d9d4c9d1f946d7947.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;它主要用来返回用例列表的数据，除了用例本身，还需要用例结果。serializers.SerializerMethodField用于自定义序列化字段，定义好字段后，在下面添加&lt;code&gt;get_前缀+字段名&lt;/code&gt;的方法，这是Django的规则，然后在方法内部添加取值代码，比如get_result()，instance是指Meta.model定义的模型，这里就等同于Case，通过instance.id获取到Case.id并赋值给case_id变量，从CaseResult的对象中过滤case_id匹配的用例结果，按运行时间倒序，取最新1条结果的result字段的值。其他字段的处理是类似的，runTime这里做了下日期格式化：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/499311186fe74d6bbcee6823a98568e9.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;第三个序列化器是CaseResultSerializer：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4b27b96941f743458fa42081b8e935fb.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;只关联了CaseResult模型的字段，runTime同样做了日期格式化。从这里也能更清晰的看到，instance就是指的自己！&lt;/p&gt;
&lt;p&gt;新建teprunner/views/case.py文件，添加视图，先把简单的两个视图做了，第一个视图是case_result：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/81e74079171b400ea9fa0cc15e2d4f84.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这里是个知识点，采用@api_view注解的函数视图，如果想用序列化器，那么采用这种方式。&lt;/p&gt;
&lt;p&gt;第二个视图是copy_case：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d4b66089366b4b229daddb4fc2a34f1a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;根据case_id查找到现有Case对象，在case.desc后面添加&lt;code&gt;--复制&lt;/code&gt;后缀，其他字段数据复用，保存，就完成复制用例了。&lt;/p&gt;
&lt;p&gt;第三个视图是CaseViewSet：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/90921afa5bca4b8cb2702cad9dd684b2.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;继承了ModelViewSet，重写了list列表查询，因为如果不重写，默认返回的结果是serializer_class = CaseSerializer序列化器的内容。我们需要的是CaseListSerializer这个序列化器的内容。这里也是一个知识点！如果想在类视图中，使用serializer_class以外的序列化器加分页，采用这种方式。&lt;/p&gt;
&lt;p&gt;重写的list方法首先添加了project_id、case_id、desc、api四个过滤条件，与前端的当前项目、用例ID搜索框、用例描述搜索框、API路径搜索框一一对应。&lt;code&gt;__icontains&lt;/code&gt;表示包含，相当于模糊匹配，&lt;code&gt;i&lt;/code&gt;忽略大小写。接着用到了自定义分页的类，按照统一的分页格式，返回序列化器的数据。&lt;/p&gt;
&lt;p&gt;最后，重写了update方法，用现有的创建人进行更新。&lt;/p&gt;
&lt;h1 id=&#34;编写前端代码&#34;&gt;编写前端代码&lt;/h1&gt;
&lt;p&gt;编辑router/index.js，添加路由：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/69b335361608450fb0556aba32e09d7c.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;编辑views/teprunner/index.vue，添加菜单项：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/640655751b4b436dac0745857a6277fb.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;新建views/teprunner/case/CaseManagement.vue文件，增删改查基础代码不再另加赘述，挑选新代码：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/2781b1acd3b94379bbcf00bd2bd680c2.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;在运行结果列，添加了超链接，点击后打开用例结果弹窗，这个弹窗查看和运行用例都会用到，用第一个参数type做区分：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4dc3075bbd7a4f8c8fa13fd55a671c43.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;getResultColor方法返回颜色样式：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/410d96b7b51e4d6dbe08cd0630826c06.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;由于用例结果弹窗和用例列表表格都会有这个字段，于是把resultColor方法提取到了utils/commonMethods.js文件中：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/0ed151d7f10d4d7eb7c372791fd864e0.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;CaseManagement.vue是一个父组件，它有3个子组件，如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/acca7566c3964a9c9054b84a6e155371.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;通过import后，添加标签代码引入：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/c0bb29a4d8d8477f931c89d133ddfa77.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/a6572cbc9616490aaa6fb03a7e576ac3.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;是否显示弹窗通过DialogFormVisible的true/false来控制，DialogTitle指定弹窗标题。&lt;code&gt;:&lt;/code&gt;开头是父组件向子组件传值， &lt;code&gt;@&lt;/code&gt;开头是接受子组件发过来的emit事件通知，从而调用父组件中方法。这样就把这几个组件绑定在一起了！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从这里开始，这几个组件之间的关系会稍微有点绕，读不懂的话多结合源码看看！&lt;/p&gt;
&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>teprunner测试平台用例前置模块开发</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000004-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E7%94%A8%E4%BE%8B%E5%89%8D%E7%BD%AE%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000004-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E7%94%A8%E4%BE%8B%E5%89%8D%E7%BD%AE%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;本文开发内容&#34;&gt;本文开发内容&lt;/h1&gt;
&lt;p&gt;现在正式进入测试相关功能开发。teprunner测试平台底层是pytest，中间层是tep，还没了解的朋友可以先看看tep的文章，整个平台的设计思路和后面用例的执行都会基于这个工具。tep的测试用例是放在.py文件里面的，全局变量或者说环境变量是引用的env_vars，公共函数和复用接口是引用的fixtures，在做成平台后，需要把这两个部分独立为两个功能模块。多个项目的接口自动化数据需要隔离开来，要有个项目管理功能。本文将开发四个用例前置模块：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;后台管理&amp;ndash;项目管理&lt;/li&gt;
&lt;li&gt;接口自动化&amp;ndash;语法说明&lt;/li&gt;
&lt;li&gt;接口自动化&amp;ndash;环境变量&lt;/li&gt;
&lt;li&gt;接口自动化&amp;ndash;fixtures&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;语法说明给出了环境变量、fixtures、用例示例（单个接口、多个接口&amp;ndash;增删改查）、请求方法（get、post、put、delete）的使用方法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;编写后端代码&#34;&gt;编写后端代码&lt;/h1&gt;
&lt;p&gt;先创建名为teprunner的app：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;django-admin startapp teprunner
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在settings的INSTALLED_APPS中添加配置让app生效：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/67d966cf73d6483297dd107366706b05.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;并在urls.py添加路由：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/f3bd4250cbe84c6a89367a48fa40ff3f.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;编辑user/fixtures/user.json，添加一个接口自动化的菜单：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/9322e66b340042d9b8304e1c9fd9bc67.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;截图只截了管理员，测试和开发角色，也需要添加上。access均为true，这三个角色都可以访问。添加后把数据同步到数据库：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py loaddata user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编辑teprunner/models.py，新增模型：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d6cffae9dd7a4a0b877eb1d08f9e07e1.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;包括了项目、环境变量和fixture三张表。Project.env_config用于给项目配置多个环境。不同项目不同环境的环境变量不一样，在EnvVar中添加了project_id和env_name，并按(&amp;ldquo;project_id&amp;rdquo;, &amp;ldquo;env_name&amp;rdquo;, &amp;ldquo;name&amp;rdquo;)作为唯一键，不允许重复。不同项目的fixtures不同，不关心环境，在Fixture中只添加了project_id。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fixture.code用于存放代码，字段类型为models.TextField，容纳更多字符。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;models编写好后，迁移到数据库：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py makemigrations
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python manage.py migrate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;新建teprunner/serializers.py文件，新增序列化器：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/cc0b2795d4c842c6a8ab80ad49c2eb37.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;数据库字段的命名是用的下划线，接口返回前端和前端传参是用的驼峰，所以这里通过source这种方式给env_config、project_id、env_name、creator_nickname进行了序列化器的重命名。为了前端更方便处理，使用serializers.CharField把int转化为了str，由于请求时id非必须，增加参数required=False。&lt;/p&gt;
&lt;p&gt;teprunner会有很多视图，写在一个文件里面有点臃肿，这里创建views文件夹，新增views/project.py项目视图：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/96cf05fb5b6f4256b5e9d6934db0c2d4.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Django REST framework的ModelViewSet视图类能节省很多代码，只需要三行就把项目增删改查接口做了。project_env是函数视图，请求方法为GET，它的作用是返回项目环境列表，当前项目和当前环境，默认为第一个项目和第一个环境。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;前端需要切换不同项目和不同环境，下拉框数据来源于这个接口。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;新建views/envvar.py新增环境变量视图：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/97f48f6c1b464ffba72d16be472fcd1d.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;环境变量跟项目和环境是强关联的，需要根据项目环境进行过滤，所以重写了list方法，从前端请求拿到curProjectId和curEnvName，通过EnvVar.objects.filter进行过滤。&lt;code&gt;from django.db.models import Q&lt;/code&gt;支持多种过滤条件，这在后面的代码中还能看到，比如模糊匹配。&lt;/p&gt;
&lt;p&gt;新建views/fixture.py新增fixture视图：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/7ea4f53b5ed1471c9c2bc6efb0d007fd.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;fixture跟项目是强关联的，需要根据项目进行过滤，所以重写了list方法，从前端请求拿到curProjectId，通过Fixture.objects.filter进行过滤。Fixture有个creatorNickname字段，新增fixture时使用的是当前登录用户的昵称，修改fixture时需要用已保存的创建者，重写update方法来实现这个处理。&lt;/p&gt;
&lt;p&gt;最后，新建teprunner/urls.py文件，把视图添加到路由中：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5a8ef67ff3fa4b1e80cbaeaa359a7248.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;编写前端代码&#34;&gt;编写前端代码&lt;/h1&gt;
&lt;p&gt;编辑package.json，添加依赖：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/faf9d8dbca8b43bbb0e7e5bffbd6185a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;fixtures是代码形式的，这是测试平台第一次使用代码编辑组件。添加依赖后记得执行&lt;code&gt;npm install&lt;/code&gt;进行安装。&lt;/p&gt;
&lt;p&gt;编辑router/index.js：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/50f53177e95740ad880a2d48d9345569.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加接口自动化的路由，并把home重定向改为语法说明路由。children表示子路由。path是路由地址。meta.title用于显示菜单名。&lt;/p&gt;
&lt;p&gt;编辑views/console/index.vue：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/b94dc3624d8b4109974bc8139f55b935.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加项目管理菜单。新建ProjectManagement.vue和AddProject.vue，添加项目管理增删改查代码，跟用户管理类似，不再另加赘述。值得注意的是环境配置&lt;code&gt;placeholder=&amp;quot;请输入环境英文名（多个环境英文逗号分隔）&amp;quot;&lt;/code&gt;，因为tep.env_vars.mapping会用到环境名作为key，所以只能用英文名！&lt;/p&gt;
&lt;p&gt;编辑components/WrapComponent.vue，添加一个插槽：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/07f2e7bbb30a4093abaef4f6b5c1acd7.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>teprunner测试平台部署到Linux系统Docker</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000003-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E9%83%A8%E7%BD%B2%E5%88%B0linux%E7%B3%BB%E7%BB%9Fdocker/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000003-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0teprunner%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E9%83%A8%E7%BD%B2%E5%88%B0linux%E7%B3%BB%E7%BB%9Fdocker/</guid>
      <description>本文是一篇过渡，在进行用例管理模块开发之前，有必要把入门篇开发完成的代码部署到Linux系统Docker中，把部署流程走一遍，这个过程对后端设计有决定性影响。</description>
    </item>
    <item>
      <title>学习版pytest内核测试平台开发万字长文入门篇</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000002-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0%E5%AD%A6%E4%B9%A0%E7%89%88pytest%E5%86%85%E6%A0%B8%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E4%B8%87%E5%AD%97%E9%95%BF%E6%96%87%E5%85%A5%E9%97%A8%E7%AF%87/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000002-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0%E5%AD%A6%E4%B9%A0%E7%89%88pytest%E5%86%85%E6%A0%B8%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E4%B8%87%E5%AD%97%E9%95%BF%E6%96%87%E5%85%A5%E9%97%A8%E7%AF%87/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;前言&#34;&gt;前言&lt;/h1&gt;
&lt;p&gt;2021年，测试平台如雨后春笋般冒了出来，我就是其中一员，写了一款pytest内核测试平台，在公司落地。分享出来后，有同学觉得挺不错，希望能开源，本着“公司代码不要传到网上去，以免引起不必要麻烦”的原则，只能在家从头写一个，边重新梳理代码边温习巩固知识点，以学习交流为目的，定义为“学习版”。&lt;/p&gt;
&lt;h1 id=&#34;功能展示&#34;&gt;功能展示&lt;/h1&gt;
&lt;h2 id=&#34;登录&#34;&gt;登录&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/a3424569c09d42aca658cf3b885278e3.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;接口自动化&#34;&gt;接口自动化&lt;/h2&gt;
&lt;p&gt;接口自动化&amp;ndash;Dashboard：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/41db611e68aa42019522249b82b90997.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接口自动化&amp;ndash;环境变量：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/896567b6022149c896fe3ee25a085775.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接口自动化&amp;ndash;fixtures：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/e78af47ce1b246888d8efec56167a2d2.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接口自动化&amp;ndash;用例管理：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/562b086acf334c8c9d716b09f40ee67e.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接口自动化&amp;ndash;用例管理&amp;ndash;编辑用例：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d9ff4d3f91374aadb002a9feac0acab9.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接口自动化&amp;ndash;测试计划：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/bac839ba20b642c484b6c61c8487711b.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接口自动化&amp;ndash;语法说明：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/e6f5f342fe4c4ca7a94aa1425116e772.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;小工具&#34;&gt;小工具&lt;/h2&gt;
&lt;p&gt;小工具&amp;ndash;共享脚本：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8d2d8773432847dabd769a1e23358c85.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;小工具&amp;ndash;HTTP状态查询：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/6abb077760834ea3b36d856bcc0dd4f8.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;后台管理&#34;&gt;后台管理&lt;/h2&gt;
&lt;p&gt;后台管理&amp;ndash;用户管理&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/82bdce6ce2b14a3694103b41159881df.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;后台管理&amp;ndash;项目管理&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/dcf99d0331e34d3ab08a233d42735b7d.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;本文开发内容&#34;&gt;本文开发内容&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;登录，登出&lt;/li&gt;
&lt;li&gt;首页，修改密码，个人信息&lt;/li&gt;
&lt;li&gt;后台管理，用户管理&lt;/li&gt;
&lt;li&gt;JWT认证&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文先打个基础，既是测试平台基本结构，也可以作为CMS基础框架，定制开发各种小型项目。&lt;/p&gt;
&lt;h1 id=&#34;技术栈&#34;&gt;技术栈&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 12.16.3&lt;/li&gt;
&lt;li&gt;Vue 4.5.11&lt;/li&gt;
&lt;li&gt;Python 3.8&lt;/li&gt;
&lt;li&gt;Django 3.1.3&lt;/li&gt;
&lt;li&gt;Django REST framework 3.12.2&lt;/li&gt;
&lt;li&gt;SQLite 3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IDE编辑器推荐PyCharm旗舰版，既能写Django也能写Vue项目。数据库使用Django自带SQLite ，省去安装MySQL和Navicat/Workbench麻烦，轻量级开发。SQLiteStudio为SQLite数据库可视化工具，只需要下载即可，无需安装，解压就用：：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://sqlitestudio.pl/&#34;&gt;https://sqlitestudio.pl/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/cf7f04cd903646ae90dadc0ab66233b0.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;由于会用到&lt;code&gt;models.JSONField&lt;/code&gt;，SQLite默认不兼容，所以需要下载&lt;code&gt;sqlite3.dll&lt;/code&gt;文件替换下：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.sqlite.org/download.html&#34;&gt;https://www.sqlite.org/download.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;根据Python版本选择，比如我的windows安装的Python38-32，下载了&lt;code&gt;sqlite-dll-win32-x86-3340100.zip&lt;/code&gt;这个软件包，解压后将&lt;code&gt;D:\Program Files (x86)\Python38-32\DLLs\sqlite3.dll&lt;/code&gt;替换。&lt;/p&gt;
&lt;h1 id=&#34;创建vue项目&#34;&gt;创建Vue项目&lt;/h1&gt;
&lt;p&gt;设置&lt;code&gt;npm&lt;/code&gt;淘宝镜像：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm config &lt;span style=&#34;color:#6639ba&#34;&gt;set&lt;/span&gt; registry https://registry.npm.taobao.org
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装&lt;code&gt;Vue CLI&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g @vue/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建&lt;code&gt;teprunner-frontend&lt;/code&gt;项目：&lt;/p&gt;</description>
    </item>
    <item>
      <title>pytest内核测试平台落地初体验</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000001-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0pytest%E5%86%85%E6%A0%B8%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E8%90%BD%E5%9C%B0%E5%88%9D%E4%BD%93%E9%AA%8C/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/teprunner/000001-%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0pytest%E5%86%85%E6%A0%B8%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E8%90%BD%E5%9C%B0%E5%88%9D%E4%BD%93%E9%AA%8C/</guid>
      <description>测试平台，有人说它鸡肋，有人说它有用，有人说它轮子，众说纷纭，不如从自身出发，考虑是否要做测试平台：</description>
    </item>
  </channel>
</rss>
