<?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>原创项目 on 测试开发刚哥</title>
    <link>https://dongfanger.github.io/categories/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/</link>
    <description>Recent content in 原创项目 on 测试开发刚哥</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Mon, 19 Jan 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://dongfanger.github.io/categories/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Python处理接口数据，这个函数太香了</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000019-python%E5%A4%84%E7%90%86%E6%8E%A5%E5%8F%A3%E6%95%B0%E6%8D%AE%E8%BF%99%E4%B8%AA%E5%87%BD%E6%95%B0%E5%A4%AA%E9%A6%99%E4%BA%86/</link>
      <pubDate>Mon, 19 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/tep/000019-python%E5%A4%84%E7%90%86%E6%8E%A5%E5%8F%A3%E6%95%B0%E6%8D%AE%E8%BF%99%E4%B8%AA%E5%87%BD%E6%95%B0%E5%A4%AA%E9%A6%99%E4%BA%86/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;python处理接口数据&#34;&gt;Python处理接口数据&lt;/h1&gt;
&lt;p&gt;接口数据通常是JSON格式，Python处理起来并不轻松。&lt;/p&gt;
&lt;p&gt;方法一，json文件。额外编写代码读取文件，接口参数很多，文件变多，难维护。&lt;/p&gt;
&lt;p&gt;方法二，字典。JSON可以直接粘贴到代码中，Python认作字典，但语法有差异，JSON的语法是true/false/null，Python的语法是True/False/None，不兼容。&lt;/p&gt;
&lt;p&gt;这两种方法都有共同的缺点，需要编写表达式来定位某个节点。&lt;/p&gt;
&lt;p&gt;比如：&lt;code&gt;body[&amp;quot;a&amp;quot;][0][&amp;quot;x&amp;quot;][&amp;quot;m&amp;quot;]&lt;/code&gt;&lt;/p&gt;
&lt;h1 id=&#34;字符串与占位符&#34;&gt;字符串与占位符&lt;/h1&gt;
&lt;p&gt;占位符是一种很常见的文本替换技术，在任何编程语言中都有广泛应用。&lt;/p&gt;
&lt;p&gt;Python语法有个很特别的字符串，三引号字符串：&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;test_json &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&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;{
&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;bool&amp;#34;: true,
&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;x&amp;#34;: null,
&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;customerId&amp;#34;: &amp;#34;123456&amp;#34;,
&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;items&amp;#34;: [
&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;    {
&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;productId&amp;#34;: &amp;#34;987654&amp;#34;,
&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;quantity&amp;#34;: 2,
&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;price&amp;#34;: 199.99
&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;    }
&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;  ],
&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;shippingAddress&amp;#34;: {
&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;recipientName&amp;#34;: &amp;#34;张三&amp;#34;,
&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;streetAddress&amp;#34;: &amp;#34;北京市朝阳区某街道123号&amp;#34;,
&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;city&amp;#34;: &amp;#34;北京&amp;#34;,
&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;state&amp;#34;: &amp;#34;北京&amp;#34;,
&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;postalCode&amp;#34;: &amp;#34;100000&amp;#34;,
&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;country&amp;#34;: &amp;#34;中国&amp;#34;
&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;  },
&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;paymentMethod&amp;#34;: {
&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;type&amp;#34;: &amp;#34;CreditCard&amp;#34;,
&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;cardNumber&amp;#34;: &amp;#34;4111111111111111&amp;#34;,
&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;expiryDate&amp;#34;: &amp;#34;12/25&amp;#34;,
&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;cvv&amp;#34;: &amp;#34;123&amp;#34;
&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;  },
&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;orderNotes&amp;#34;: &amp;#34;请尽快发货，谢谢！&amp;#34;
&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;}
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;基本思路是，把JSON写在三引号字符串中，这能保留原生JSON格式，就像很多测试平台的做法一样，然后再通过占位符，直接替换其中的节点值。占位符不用编写表达式定位节点，在JSON字符串标记即可。&lt;/p&gt;</description>
    </item>
    <item>
      <title>年度大版本，tep3回归纯粹Python接口测试框架</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000018-%E5%B9%B4%E5%BA%A6%E5%A4%A7%E7%89%88%E6%9C%ACtep3%E5%9B%9E%E5%BD%92%E7%BA%AF%E7%B2%B9python%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6/</link>
      <pubDate>Sun, 18 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/tep/000018-%E5%B9%B4%E5%BA%A6%E5%A4%A7%E7%89%88%E6%9C%ACtep3%E5%9B%9E%E5%BD%92%E7%BA%AF%E7%B2%B9python%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6/</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;tep是基于pytest开发的接口测试框架，上个版本从小工具升级为了关键字驱动框架，通过pystats发现最近半年仍然有4000+人次在安装使用tep。时隔1年后，我对tep进行了一次大版本改造，抛弃tep2的Java代码风格，重新回归到tep1的纯粹Python风格。&lt;/p&gt;
&lt;h1 id=&#34;纯粹python&#34;&gt;纯粹Python&lt;/h1&gt;
&lt;p&gt;所谓纯粹Python，体现在两个方面，一个方面是tep源码进行了重写，更加Pythonic，自动化用例写起来也更加简单易懂；另一方面是抛弃了依赖Java的Allure和依赖C++的loguru，分别使用Python技术栈的pytest-html和logging替代。&lt;/p&gt;
&lt;h1 id=&#34;tep3版本&#34;&gt;tep3版本&lt;/h1&gt;
&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;tep是Try Easy Pytest的首字母缩写，帮你轻松上手pytest。&lt;/p&gt;
&lt;p&gt;框架特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基于pytest封装，成熟、稳定且扩展性强。&lt;/li&gt;
&lt;li&gt;框架完全由Python构建，没有混杂其他语言。&lt;/li&gt;
&lt;li&gt;原生Python语法，学习Python，零成本使用框架。&lt;/li&gt;
&lt;li&gt;HAR包转换pytest用例。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;p&gt;支持Python3.8以上版本&lt;/p&gt;
&lt;p&gt;创建虚拟环境：&lt;code&gt;python -m venv venv&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;激活虚拟环境，Windows用户：&lt;code&gt;activate.bat&lt;/code&gt; Mac用户：&lt;code&gt;source venv/bin/activate&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;安装tep：&lt;code&gt;pip install tep&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;验证安装成功：&lt;code&gt;tep -V&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;目录结构&#34;&gt;目录结构&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;data
file
fixture
report
scripts
tests
.gitignore
conftest.py
pytest.ini
run.py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;tep new demo&lt;/code&gt;就能快速创建项目名称为&lt;code&gt;demo&lt;/code&gt;的项目脚手架。&lt;/p&gt;
&lt;h2 id=&#34;用例示范&#34;&gt;用例示范&lt;/h2&gt;
&lt;p&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-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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;tep&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;test&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;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;http://httpbin.org/status/200&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;200&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;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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;tep&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; request
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;tep&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;data.global_data&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; GlobalData
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;Data&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;    v&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;domain&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; GlobalData&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;domain&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;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;2&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;payAmount&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;0.2&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;    body_add_cart &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;{&amp;#34;skuId&amp;#34;:&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{skuId}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;,&amp;#34;skuNum&amp;#34;:$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{skuNum}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body_order &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;{&amp;#34;skuId&amp;#34;:&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{skuId}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;,&amp;#34;price&amp;#34;:$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{skuPrice}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;,&amp;#34;skuNum&amp;#34;:$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{skuNum}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;,&amp;#34;totalPrice&amp;#34;:$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{totalPrice}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body_pay &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;{&amp;#34;orderId&amp;#34;:&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{orderId}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;,&amp;#34;payAmount&amp;#34;:$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{payAmount}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;test&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;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{domain}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;/searchSku?skuName=book&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;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;GlobalData&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;headers&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&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;    v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.price&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{domain}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;/addCart&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;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Data&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;body_add_cart&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;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;GlobalData&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&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;    v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;totalPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.totalPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{domain}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;/order&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;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Data&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;body_order&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;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;GlobalData&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;orderId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.orderId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{domain}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;/pay&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;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; v&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Data&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;body_pay&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;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;GlobalData&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.success&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;用例和数据&#34;&gt;用例和数据&lt;/h1&gt;
&lt;p&gt;数据怎么存放，这里给出了一个参考方案，在用例内定义&lt;code&gt;class Data&lt;/code&gt;，然后通过类变量存放数据。不同的项目，不同的场景，不同的参数，可以考虑不一样的存放方式。&lt;/p&gt;</description>
    </item>
    <item>
      <title>tep1.0.0教程</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000017-tep1.0.0%E6%95%99%E7%A8%8B/</link>
      <pubDate>Sat, 17 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/tep/000017-tep1.0.0%E6%95%99%E7%A8%8B/</guid>
      <description>正式版教程： &lt;a href=&#34;https://dongfanger.gitee.io/blog/tep.html&#34;&gt;https://dongfanger.gitee.io/blog/tep.html&lt;/a&gt;</description>
    </item>
    <item>
      <title>当Pytest遇上MVC分层设计自动化用例就该这么写</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000016-%E5%BD%93pytest%E9%81%87%E4%B8%8Amvc%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E7%94%A8%E4%BE%8B%E5%B0%B1%E8%AF%A5%E8%BF%99%E4%B9%88%E5%86%99/</link>
      <pubDate>Fri, 16 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/tep/000016-%E5%BD%93pytest%E9%81%87%E4%B8%8Amvc%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E7%94%A8%E4%BE%8B%E5%B0%B1%E8%AF%A5%E8%BF%99%E4%B9%88%E5%86%99/</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;数据写在代码里，追求快速编写用例，是我设计tep的一个特点，这在个人编写时是一种非常良好的体验。但相比于HttpRunner、JMeter等来说，总觉得还差点意思。思考良久，总结为三个字：&lt;strong&gt;工程化&lt;/strong&gt;。工程化是我近一年在学习Java并参与了2个测试平台模块开发，和写了几个小工具后，感受到的一种编程思想。而其中最明显的就是Spring的MVC分层设计。为了让tep更工程化，后续版本将以MVC模块编写用例为准，同时会兼容之前的脚本式快速编写。&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;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5487a3073adf417c82931654f233f508.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;test_case：用例主程序；&lt;/p&gt;
&lt;p&gt;steps：测试步骤；&lt;/p&gt;
&lt;p&gt;data：纯粹的json；&lt;/p&gt;
&lt;h2 id=&#34;测试用例&#34;&gt;测试用例&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;test_case.py&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-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;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;allure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;examples.tests.LoginToPayFlow.steps.step_add_cart&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; step_add_cart
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;examples.tests.LoginToPayFlow.steps.step_order&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; step_order
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;examples.tests.LoginToPayFlow.steps.step_pay&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; step_pay
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;examples.tests.LoginToPayFlow.steps.step_search_sku&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; step_search_sku
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.cache&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; TepCache
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.step&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; Step
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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;&amp;#34;&amp;#34;
&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;测试登录到下单流程，需要先运行utils/fastapi_mock.py
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#0550ae&#34;&gt;@allure.title&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;从登录到下单支付&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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;login&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; env_vars&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; case_vars&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;    case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;put&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;token&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; login&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;token&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;    cache &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; TepCache&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;env_vars&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;env_vars&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;case_vars&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Step&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;搜索商品&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; step_search_sku&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; cache&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;    Step&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;添加购物车&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; step_add_cart&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; cache&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;    Step&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;下单&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; step_order&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; cache&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;    Step&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;支付&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; step_pay&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; cache&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;TepCache是“缓存”，包括global_vars、env_vars、case_vars三个级别的变量池。&lt;/p&gt;
&lt;p&gt;Step是一个泛化调用类，作用是打日志，调用第二个参数对应的步骤函数。&lt;/p&gt;
&lt;h2 id=&#34;测试步骤&#34;&gt;测试步骤&lt;/h2&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.cache&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; TepCache
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.func&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; data
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.http_client&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;step_search_sku&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;cache&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; TepCache&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;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;env_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;/searchSku&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#0550ae&#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;token&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;token&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;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; data&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;查询SKU.json&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; params&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;put&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.skuId&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;    cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;put&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.price&amp;#34;&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;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-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.cache&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; TepCache
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.func&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; data
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;utils.http_client&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;step_add_cart&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;cache&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; TepCache&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;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;env_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;/addCart&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    headers &lt;span style=&#34;color:#0550ae&#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;token&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;token&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;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; data&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;添加购物车.json&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;put&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.skuNum&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;    cache&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;case_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;put&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;totalPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.totalPrice&amp;#34;&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;p&gt;步骤函数以step开头，尤其注意的是cache借助Python Typing提示，可以在编写下面代码时，获得PyCharm语法提示，所以一定不要忘了加上&lt;code&gt;: TepCache&lt;/code&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>tep支持pytest-xdist分布式执行用例及合并Allure报告</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000015-tep%E6%94%AF%E6%8C%81pytest-xdist%E5%88%86%E5%B8%83%E5%BC%8F%E6%89%A7%E8%A1%8C%E7%94%A8%E4%BE%8B%E5%8F%8A%E5%90%88%E5%B9%B6allure%E6%8A%A5%E5%91%8A/</link>
      <pubDate>Thu, 15 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/tep/000015-tep%E6%94%AF%E6%8C%81pytest-xdist%E5%88%86%E5%B8%83%E5%BC%8F%E6%89%A7%E8%A1%8C%E7%94%A8%E4%BE%8B%E5%8F%8A%E5%90%88%E5%B9%B6allure%E6%8A%A5%E5%91%8A/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;tep近期更新频率较快，一方面是作者在积极投入到tep工具开发中；另一方面是我们聚集了20位小伙伴，一起合力打造&lt;strong&gt;EasyPytest测试平台&lt;/strong&gt;，teprunner的FastAPI升级版本，依托于tep，&lt;strong&gt;帮你高效管理pytest测试用例&lt;/strong&gt;。陆续也会有平台开发日志发布，欢迎持续关注。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;tep0.11.1版本升级内容：&lt;/p&gt;
&lt;p&gt;1、pytest-xdist分布式执行用例，合并Allure报告；&lt;/p&gt;
&lt;p&gt;2、global_vars全局变量配置；&lt;/p&gt;
&lt;h1 id=&#34;分布式执行用例&#34;&gt;分布式执行用例&lt;/h1&gt;
&lt;p&gt;借助于pytest-xdist，在命令行执行时添加参数&lt;code&gt;-n auto&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest &lt;span style=&#34;color:#0550ae&#34;&gt;-&lt;/span&gt;n auto
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;pytest-xdist会自动根据本地机器硬件配置，设置最优并发，并分发用例，分布式执行。&lt;/p&gt;
&lt;p&gt;测试时先运行main.py，然后在case100目录下执行&lt;code&gt;pytest -n auto --tep-reports&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/1076643eec9e418685bdef10b49e53a5.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;第一次串行，第二次xdist并行：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/55a2c725e31e47ac8b2abebd886a3abe.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;执行时间从50s一下降到5s，性能提升还是非常的明显。&lt;/p&gt;
&lt;h1 id=&#34;合并allure报告&#34;&gt;合并Allure报告&lt;/h1&gt;
&lt;p&gt;pytest-xdist分布式执行，只要把allure源文件，也就是那一堆json文件，存到同一个目录下，报告的数据就是一体的，不需要单独合并。但是有个问题，tep封装了&lt;code&gt;--tep-reports&lt;/code&gt; 命令行参数一键生成Allure报告，背后的逻辑是在pytest_sessionfinish hook函数里面实现的，分布式执行时，每个xdist的node都是一个单独的session，多个node就会生成多份报告：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8c44989d22914ca68894d80d1aa07d4c.png&#34;&gt;c&lt;/p&gt;
&lt;p&gt;10个node生成了11份报告，其中1份master节点生成的。pytest-xdist的原理如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/03de9a932bac4e3c8bb9d9cdc55cf0a3.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;master节点不运行任何测试，只是通过一小部分消息与节点通信。子节点执行后会通过workeroutput把数据回传给master节点。所以只需要通过是否有workeroutput属性来判断master节点：&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;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;_is_master&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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;&amp;#34;&amp;#34;
&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;    pytest-xdist分布式执行时，判断是主节点master还是子节点
&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;    主节点没有workerinput属性
&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;&amp;#34;&amp;#34;&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;hasattr&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;workerinput&amp;#39;&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;p&gt;然后只在主节点的pytest_sessionfinish，生成1次报告，就能避免生成多份报告。这样在xdist分布式执行模式下，&lt;code&gt;--tep-reports&lt;/code&gt;也只会生成1份合并后的包含所有测试用例的Allure HTML报告。&lt;/p&gt;
&lt;p&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-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;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;time&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:#cf222e&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;shutil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;allure_commons&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:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;allure_commons.logger&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; AllureFileLogger
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;allure_pytest.listener&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; AllureListener
&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;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;allure_pytest.plugin&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; cleanup_factory
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;reports_path &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Project&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;root_dir&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;reports&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;allure_source_dir_name &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;.allure.source.temp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;allure_source_path &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;reports_path&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; allure_source_dir_name&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;_tep_reports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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;&amp;#34;&amp;#34;
&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;    --tep-reports命令行参数不能和allure命令行参数同时使用，否则可能出错
&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;&amp;#34;&amp;#34;&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:#cf222e&#34;&gt;if&lt;/span&gt; config&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;getoption&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;--tep-reports&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; config&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;getoption&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;allure_report_dir&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;True&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;_is_master&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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;&amp;#34;&amp;#34;
&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;    pytest-xdist分布式执行时，判断是主节点master还是子节点
&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;    主节点没有workerinput属性
&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;&amp;#34;&amp;#34;&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;hasattr&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;workerinput&amp;#39;&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;pytest_addoption&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;parser&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;&amp;#34;&amp;#34;
&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;    allure测试报告 命令行参数
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parser&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;addoption&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;--tep-reports&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;        action&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;store_const&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;        const&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;True&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;        help&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Create tep allure HTML reports.&amp;#34;&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;pytest_configure&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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;&amp;#34;&amp;#34;
&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;    这段代码源自：https://github.com/allure-framework/allure-python/blob/master/allure-pytest/src/plugin.py
&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;    目的是生成allure源文件，用于生成HTML报告
&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;&amp;#34;&amp;#34;&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:#cf222e&#34;&gt;if&lt;/span&gt; _tep_reports&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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:#cf222e&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;exists&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;allure_source_path&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;            shutil&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;rmtree&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;allure_source_path&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;        test_listener &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; AllureListener&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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;        config&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;pluginmanager&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;register&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;test_listener&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;        allure_commons&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;plugin_manager&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;register&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;test_listener&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;        config&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;add_cleanup&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;cleanup_factory&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;test_listener&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        clean &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; config&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;option&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;clean_alluredir
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        file_logger &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; AllureFileLogger&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;allure_source_path&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; clean&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;  &lt;span style=&#34;color:#57606a&#34;&gt;# allure_source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        allure_commons&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;plugin_manager&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;register&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;file_logger&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;        config&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;add_cleanup&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;cleanup_factory&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;file_logger&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;pytest_sessionfinish&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;session&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;&amp;#34;&amp;#34;
&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;    测试运行结束后生成allure报告
&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;&amp;#34;&amp;#34;&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:#cf222e&#34;&gt;if&lt;/span&gt; _tep_reports&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;session&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;config&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:#cf222e&#34;&gt;if&lt;/span&gt; _is_master&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;session&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;config&lt;span style=&#34;color:#1f2328&#34;&gt;):&lt;/span&gt;  &lt;span style=&#34;color:#57606a&#34;&gt;# 只在master节点才生成报告&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:#57606a&#34;&gt;# 最近一份报告的历史数据，填充allure趋势图&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:#cf222e&#34;&gt;if&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;exists&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;reports_path&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;                his_reports &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;listdir&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;reports_path&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:#cf222e&#34;&gt;if&lt;/span&gt; allure_source_dir_name &lt;span style=&#34;color:#0550ae&#34;&gt;in&lt;/span&gt; his_reports&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;                    his_reports&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;remove&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;allure_source_dir_name&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:#cf222e&#34;&gt;if&lt;/span&gt; his_reports&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;                    latest_report_history &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;reports_path&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; his_reports&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:#0550ae&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;],&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;history&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;                    shutil&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;copytree&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;latest_report_history&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;allure_source_path&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;history&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            current_time &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; time&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;strftime&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;%d&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;-%H-%M-%S&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; time&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;localtime&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;time&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;time&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;            html_report_name &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;reports_path&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;report-&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;+&lt;/span&gt; current_time&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;            os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;system&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;allure generate &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;allure_source_path&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt; -o &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;html_report_name&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;  --clean&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;            shutil&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;rmtree&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;allure_source_path&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;h1 id=&#34;global_vars全局变量&#34;&gt;global_vars全局变量&lt;/h1&gt;
&lt;p&gt;在使用环境变量模版时，有些变量需要在多个模版重复设置，tep新增了global_vars全局变量fixture，可以将重复设置的变量，定义在&lt;code&gt;resources/global_vars.yaml&lt;/code&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>tep时隔8个月迎来重大全新升级</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000014-tep%E6%97%B6%E9%9A%948%E4%B8%AA%E6%9C%88%E8%BF%8E%E6%9D%A5%E9%87%8D%E5%A4%A7%E5%85%A8%E6%96%B0%E5%8D%87%E7%BA%A7/</link>
      <pubDate>Wed, 14 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/tep/000014-tep%E6%97%B6%E9%9A%948%E4%B8%AA%E6%9C%88%E8%BF%8E%E6%9D%A5%E9%87%8D%E5%A4%A7%E5%85%A8%E6%96%B0%E5%8D%87%E7%BA%A7/</guid>
      <description>&lt;strong&gt;tep此次更新，旨在从“工具”升级为“框架”，为此做了大量的代码整洁工作，重新设计了部分功能，项目脚手架也焕然一新。&lt;/strong&gt;</description>
    </item>
    <item>
      <title>接口自动化项目落地之HTTPBin网站</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000013-%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96%E9%A1%B9%E7%9B%AE%E8%90%BD%E5%9C%B0%E4%B9%8Bhttpbin%E7%BD%91%E7%AB%99/</link>
      <pubDate>Tue, 13 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/tep/000013-%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96%E9%A1%B9%E7%9B%AE%E8%90%BD%E5%9C%B0%E4%B9%8Bhttpbin%E7%BD%91%E7%AB%99/</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;找个开源网站或开源项目，用&lt;strong&gt;tep&lt;/strong&gt;实现整套&lt;strong&gt;pytest&lt;/strong&gt;接口自动化项目落地，归档到电子书，作为&lt;strong&gt;tep完整教程&lt;/strong&gt;的&lt;strong&gt;项目篇&lt;/strong&gt;一部分。自从tep完整教程发布以后，tep被越来越多小伙伴了解。教程只是纯理论，是骡子是马，拉出来遛遛才知道。做接口自动化项目落地，一方面是为了让自己脑海中的构想实实在在的呈现出来，现实和理想存在多少差距，不断尝试去弥补和修缮；另一方面也是方便读者朋友们学习使用，借助实际项目来练习，才能在赛道中弯道超车。&lt;/p&gt;
&lt;h1 id=&#34;httpbin网站&#34;&gt;HTTPBin网站&lt;/h1&gt;
&lt;p&gt;httpbin.org是一个简单的在线提供HTTP服务的网站：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/216043454b2f43eeb4659ce9ac7b81a8.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/acfb96edb6fe40008c3942d17482d2ee.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;它能够用来对HTTP进行在线测试。&lt;/p&gt;
&lt;h1 id=&#34;测试报告&#34;&gt;测试报告&lt;/h1&gt;
&lt;p&gt;HTTPBin网站的接口自动化项目包含&lt;strong&gt;11个用例集&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/2b7c958062ed47d89e09e9e40d6abb5c.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;67条测试用例&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/c63e2d9a06bc4bdd8e7feed8b7ebc349.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;自动化执行正确率98.5%&lt;/strong&gt;，其中有1条错误结果，是我故意为之的，因为想展示下断言失败的效果。&lt;/p&gt;
&lt;h1 id=&#34;环境配置&#34;&gt;环境配置&lt;/h1&gt;
&lt;p&gt;包含http和https两套环境，因为HTTPBin支持HTTP&amp;amp;HTTPS：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;fixtures/fixture_env_vars.py&lt;/em&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;#!/usr/bin/python&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:#57606a&#34;&gt;## encoding=utf-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;tep.fixture&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#0550ae&#34;&gt;@pytest.fixture&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;scope&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;session&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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;env_vars&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;config&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:#cf222e&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;Clazz&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;TepVars&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;        env &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; config&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;env&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&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;&amp;#34;&amp;#34;变量定义开始&amp;#34;&amp;#34;&amp;#34;&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:#57606a&#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:#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;http&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:#57606a&#34;&gt;# http环境&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;domain&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;http://httpbin.org&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:#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;https&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:#57606a&#34;&gt;# https环境&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;domain&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;https://httpbin.org&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:#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:#57606a&#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:#57606a&#34;&gt;# 定义类属性，敲代码时会自动补全&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        domain &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; mapping&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;env&lt;span style=&#34;color:#1f2328&#34;&gt;][&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&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;&amp;#34;&amp;#34;变量定义结束&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;return&lt;/span&gt; Clazz&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;配置默认为http环境：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;conf.yaml&lt;/em&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-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;env&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;http&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;用例集&#34;&gt;用例集&lt;/h1&gt;
&lt;h2 id=&#34;http-methods&#34;&gt;http-methods&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/85caafcedc6e4c3bb04f47375fbf8015.png&#34;&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-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;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;allure&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:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;tep.client&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#0550ae&#34;&gt;@allure.title&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;get请求&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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;env_vars&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:#57606a&#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:#57606a&#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:#57606a&#34;&gt;# 请求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; request&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;get&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;        url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;env_vars&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;domain &lt;span style=&#34;color:#0550ae&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;/get&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;        headers&lt;span style=&#34;color:#0550ae&#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;#39;Host&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;httpbin.org&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;Proxy-Connection&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;keep-alive&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;accept&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;application/json&amp;#39;&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;#39;User-Agent&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36&amp;#39;&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;#39;Referer&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;http://httpbin.org/&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;Accept-Encoding&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;gzip, deflate&amp;#39;&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;#39;Accept-Language&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7&amp;#39;&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;        params&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:#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:#57606a&#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:#57606a&#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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;auth&#34;&gt;auth&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/30f73a425db04174bb36bfcb19e79800.png&#34;&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/tep/000012-%E6%B0%91%E9%97%B4%E6%B5%81%E8%BD%AC%E7%9A%84pytest%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1/</link>
      <pubDate>Mon, 12 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/tep/000012-%E6%B0%91%E9%97%B4%E6%B5%81%E8%BD%AC%E7%9A%84pytest%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d890fa29667248acb99bcd84bb794f0a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;创建上图的pytest项目结构，&lt;strong&gt;只需要一条命令即可&lt;/strong&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;tep startproject demo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8ef1c1ae74b04a1295e9a3c91a44aeac.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;前提是安装tep0.9.8版本：&lt;/strong&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;pip install &lt;span style=&#34;color:#953800&#34;&gt;tep&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;==&lt;/span&gt;0.9.8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;示例代码单独放在了samples文件夹下，可以放心将整个目录删除。其中的db演示了连接MySQL数据库，http演示了requests常见用法和tep request的猴子补丁，login_pay演示了&lt;strong&gt;从登陆到下单该如何编写脚本&lt;/strong&gt;，包括httprunner、mvc（不推荐）、tep（强烈推荐）三种写法。&lt;/p&gt;
&lt;p&gt;fixtures从conftest.py文件中单独拎了出来。这个设计与Django不谋而合，Django也有个叫做fixtures的文件夹，用来存放测试数据。其实pytest的fixture的本意是可以用来做setup和teardown的，从这个意义来说，fixtures就是存放测试数据的，比如封装登录接口为用例提供token。&lt;/p&gt;
&lt;p&gt;utils存放了一些工具。比如自带的fastapi项目，request猴子补丁，mitmproxy抓包自动生成用例。request猴子补丁可以自定义日志输出格式，在发送请求前后完成任何你想做的操作。&lt;/p&gt;
&lt;p&gt;tep0.9.8版本正式发布，对项目脚手架进行了精简，是为接下来的**《tep完整教程帮你突破pytest》**做好准备。&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/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>tep集成mitmproxy录制流量自动生成用例</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000011-tep%E9%9B%86%E6%88%90mitmproxy%E5%BD%95%E5%88%B6%E6%B5%81%E9%87%8F%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E7%94%A8%E4%BE%8B/</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/tep/000011-tep%E9%9B%86%E6%88%90mitmproxy%E5%BD%95%E5%88%B6%E6%B5%81%E9%87%8F%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%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;操作过程非常简单，流程如下：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/878aab2dbe594c36b9daadf8c46baf8b.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;①&lt;strong&gt;配置过滤域名&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;必须配置，不然会有很多无效流量造成数据冗余。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/aecdc4689cd145c5b79e049fbc5e18e1.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;②&lt;strong&gt;启动代理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;「示例」使用了反向代理，tep自带FastApi启动Mock服务：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5477922c30864b6686005cac4c30d1a0.png&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「&lt;strong&gt;实际&lt;/strong&gt;」使用正向代理，并手动配置代理。命令说明在&lt;code&gt;utils/mitm.py&lt;/code&gt;文件中有：&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4a6bbaf69b9c4321960f445ff9c07b4e.png&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;③&lt;strong&gt;录制流量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;「示例」通过Postman发起请求：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/e75f4777b1514b22bd450704670603bf.png&#34;&gt;&lt;/p&gt;
&lt;blockquote&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/cf5ebc04eea34b0cbd710dcb35b9d378.png&#34;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;自动生成用例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用例就在&lt;code&gt;tests/mitm&lt;/code&gt;&lt;strong&gt;实时生成&lt;/strong&gt;好了，用例文件名为当前时间：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/423aec3928b24f778066d05885ebd279.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;每录制一个请求，就能在测试用例中看到实时添加了一条测试步骤：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    # 接口描述
    # 数据
    # 请求
    response = request(
        &amp;#34;{method}&amp;#34;,
        url=&amp;#34;{url}&amp;#34;,
        headers={headers},
        {body_grammar}
    )
    # 数据提取
    # var = response.jmespath(&amp;#34;expression&amp;#34;)
    # 断言
    assert response.status_code &amp;lt; 400
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;录制完成以后，就可以再次编辑测试用例完善它。&lt;/p&gt;
&lt;h1 id=&#34;原理&#34;&gt;原理&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;utils/mitm.py&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;#!/usr/bin/python&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:#57606a&#34;&gt;## encoding=utf-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#57606a&#34;&gt;## mitmproxy录制流量自动生成用例&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;os&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:#cf222e&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;mitmproxy&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; ctx
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;project_dir &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;dirname&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;dirname&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;abspath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;__file__&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;tests_dir &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;project_dir&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;tests&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:#57606a&#34;&gt;## tests/mitm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mitm_dir &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;tests_dir&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;mitm&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:#cf222e&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;exists&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;mitm_dir&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;    os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;mkdir&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;mitm_dir&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:#57606a&#34;&gt;## 当前时间作为文件名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;filename &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; time&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;strftime&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;%Y%m&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;%d&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;_%H_%M_%S&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; time&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;localtime&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:#0a3069&#34;&gt;&amp;#34;.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;case_file &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;join&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;mitm_dir&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; filename&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:#57606a&#34;&gt;## 生成用例文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;template &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&amp;#34;from tep.client import request
&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;
&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;
&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;def test():
&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;&amp;#34;&amp;#34;&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:#cf222e&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; os&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;path&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;exists&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;case_file&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:#cf222e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;open&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;case_file&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;w&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; encoding&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;utf8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;as&lt;/span&gt; fw&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;        fw&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;write&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;template&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;Record&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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;__init__&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; domains&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:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;domains &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; domains
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;response&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; flow&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:#cf222e&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;match&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;flow&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;url&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:#57606a&#34;&gt;# method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            method &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; flow&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;method&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;lower&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;            ctx&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;log&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;method&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:#57606a&#34;&gt;# url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; flow&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;url
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ctx&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;log&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;url&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:#57606a&#34;&gt;# headers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            headers &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;dict&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;flow&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;headers&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;            ctx&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;log&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;headers&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:#57606a&#34;&gt;# body&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; flow&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;request&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;text &lt;span style=&#34;color:#0550ae&#34;&gt;or&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;            ctx&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;log&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;body&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:#cf222e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;open&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;case_file&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; encoding&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;utf8&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;as&lt;/span&gt; fa&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;                fa&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;write&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;step&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;method&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; body&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;match&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&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:#cf222e&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;domains&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;            ctx&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;log&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;error&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;必须配置过滤域名&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;            exit&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:#0550ae&#34;&gt;1&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:#cf222e&#34;&gt;for&lt;/span&gt; domain &lt;span style=&#34;color:#0550ae&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;domains&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:#cf222e&#34;&gt;if&lt;/span&gt; domain &lt;span style=&#34;color:#0550ae&#34;&gt;in&lt;/span&gt; url&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;True&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;step&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; method&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; body&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:#cf222e&#34;&gt;if&lt;/span&gt; method &lt;span style=&#34;color:#0550ae&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;get&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;            body_grammar &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;params=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;body&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&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:#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;            body_grammar &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;json=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;body&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&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:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&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;    # 接口描述
&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;    # 数据
&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;    # 请求
&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;    response = request(
&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;&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;method&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;,
&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;        url=&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;url&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;,
&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;        headers=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;headers&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#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;        &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;body_grammar&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#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;    )
&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;    # 数据提取
&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;    # var = response.jmespath(&amp;#34;expression&amp;#34;)
&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;    # 断言
&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;    assert response.status_code &amp;lt; 400
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#57606a&#34;&gt;## ==================================配置开始==================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;addons &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;    Record&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:#57606a&#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:#0a3069&#34;&gt;&amp;#34;http://www.httpbin.org&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;http://127.0.0.1:5000&amp;#34;&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;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;## ==================================配置结束==================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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;&amp;#34;&amp;#34;
&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;==================================命令说明开始==================================
&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;## 正向代理
&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;mitmdump -s mitm.py
&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;## 反向代理
&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;mitmdump -s mitm.py --mode reverse:http://127.0.0.1:5000 --listen-host 127.0.0.1 --listen-port 8000
&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;==================================命令说明结束==================================
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过mitmproxy命令启动代理后，获取当前时间作为文件名在&lt;code&gt;tests/mitm&lt;/code&gt;下生成用例文件，给addons添加插件Record类，通过response钩子捕获请求信息，对请求进行域名过滤，域名匹配成功的请求就使用测试步骤代码模板，填充method、url、headers、body数据，追加到测试用例文件中。&lt;/p&gt;</description>
    </item>
    <item>
      <title>tep0.9.5支持自定义扩展request</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000010-tep0.9.5%E6%94%AF%E6%8C%81%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95request/</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/tep/000010-tep0.9.5%E6%94%AF%E6%8C%81%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95request/</guid>
      <description>tep0.9.5更新了以下内容：</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>tep支持MVC架构实现用例数据分离</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000009-tep%E6%94%AF%E6%8C%81mvc%E6%9E%B6%E6%9E%84%E5%AE%9E%E7%8E%B0%E7%94%A8%E4%BE%8B%E6%95%B0%E6%8D%AE%E5%88%86%E7%A6%BB/</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/tep/000009-tep%E6%94%AF%E6%8C%81mvc%E6%9E%B6%E6%9E%84%E5%AE%9E%E7%8E%B0%E7%94%A8%E4%BE%8B%E6%95%B0%E6%8D%AE%E5%88%86%E7%A6%BB/</guid>
      <description>大家好，我是刚哥。</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>tep集成HttpRunner与Flask实现开箱即用</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000008-tep%E9%9B%86%E6%88%90httprunner%E4%B8%8Eflask%E5%AE%9E%E7%8E%B0%E5%BC%80%E7%AE%B1%E5%8D%B3%E7%94%A8/</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/tep/000008-tep%E9%9B%86%E6%88%90httprunner%E4%B8%8Eflask%E5%AE%9E%E7%8E%B0%E5%BC%80%E7%AE%B1%E5%8D%B3%E7%94%A8/</guid>
      <description>大家好，我是刚哥。</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>原创pytest辅助工具tep0.9.1版本恢复项目初始化文件</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000007-%E5%8E%9F%E5%88%9Bpytest%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7tep0.9.1%E7%89%88%E6%9C%AC%E6%81%A2%E5%A4%8D%E9%A1%B9%E7%9B%AE%E5%88%9D%E5%A7%8B%E5%8C%96%E6%96%87%E4%BB%B6/</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/tep/000007-%E5%8E%9F%E5%88%9Bpytest%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7tep0.9.1%E7%89%88%E6%9C%AC%E6%81%A2%E5%A4%8D%E9%A1%B9%E7%9B%AE%E5%88%9D%E5%A7%8B%E5%8C%96%E6%96%87%E4%BB%B6/</guid>
      <description>最近几个月时间有些小伙伴问到为什么tep项目初始化文件没有了？</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>tep环境变量、fixtures、用例三者之间的关系</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000006-tep%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8Ffixtures%E7%94%A8%E4%BE%8B%E4%B8%89%E8%80%85%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB/</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/tep/000006-tep%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8Ffixtures%E7%94%A8%E4%BE%8B%E4%B8%89%E8%80%85%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB/</guid>
      <description>tep是一款测试工具，在pytest测试框架基础上集成了第三方包，提供项目脚手架，帮助以写Python代码方式，快速实现自动化项目落地。</description>
    </item>
    <item>
      <title>tep0.6.0更新聊聊pytest变量接口用例3个级别复用</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000005-tep0.6.0%E6%9B%B4%E6%96%B0%E8%81%8A%E8%81%8Apytest%E5%8F%98%E9%87%8F%E6%8E%A5%E5%8F%A3%E7%94%A8%E4%BE%8B3%E4%B8%AA%E7%BA%A7%E5%88%AB%E5%A4%8D%E7%94%A8/</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/tep/000005-tep0.6.0%E6%9B%B4%E6%96%B0%E8%81%8A%E8%81%8Apytest%E5%8F%98%E9%87%8F%E6%8E%A5%E5%8F%A3%E7%94%A8%E4%BE%8B3%E4%B8%AA%E7%BA%A7%E5%88%AB%E5%A4%8D%E7%94%A8/</guid>
      <description>tep是一款测试工具，在pytest测试框架基础上集成了第三方包，提供项目脚手架，帮助以写Python代码方式，快速实现自动化项目落地。fixture是pytest核心技术，本文聊聊如何使用fixture完成变量、接口、用例3个级别复用。</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>如何用tep完成增删改查接口自动化</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000004-%E5%A6%82%E4%BD%95%E7%94%A8tep%E5%AE%8C%E6%88%90%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96/</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/tep/000004-%E5%A6%82%E4%BD%95%E7%94%A8tep%E5%AE%8C%E6%88%90%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96/</guid>
      <description>tep的设计理念是让人人都可以用Python写自动化，本文就来介绍如何用tep完成增删改查接口自动化。</description>
    </item>
    <item>
      <title>pytestx容器化执行引擎</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/pytestx/000003-pytestx%E5%AE%B9%E5%99%A8%E5%8C%96%E6%89%A7%E8%A1%8C%E5%BC%95%E6%93%8E/</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/pytestx/000003-pytestx%E5%AE%B9%E5%99%A8%E5%8C%96%E6%89%A7%E8%A1%8C%E5%BC%95%E6%93%8E/</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;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5a917ce9454040089ddd36f52930b7b6.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;前端、后端、pytest均以&lt;strong&gt;Docker&lt;/strong&gt;容器运行服务，单独的容器化执行引擎，项目环境隔离，即用即取，用完即弃，简单，高效。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前端容器：页面交互，请求后端，展示HTML报告&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后端容器：接收前端请求，启动任务，构建镜像，触发运行pytest，挂载HTML报告&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;pytest容器：拉取项目代码，指定目录执行，生成HTML报告&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;说明：构建镜像目前是在宿主机启动后端服务来执行docker命令的，暂未支持Kubernetes编排。宿主机安装了Docker，启动服务后，可以执行docker命令。如果采用容器部署后端，容器里面不包含Docker，无法构建，个人想法是可以借助K8S来编排，当前版本还未实现&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;系统流程&#34;&gt;系统流程&lt;/h1&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/f3705e59f74a44b3b1c49d38776cf513.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;支持2种运行模式配置：容器和本地。&lt;/p&gt;
&lt;p&gt;容器模式：判断是否支持docker，如果支持，构建pytest镜像，在构建时，通过git拉取项目代码，再运行容器，按照指定目录执行pytest，生成测试报告，并将报告文件挂载到后端。如果不支持，降级为本地运行。&lt;/p&gt;
&lt;p&gt;本地模式：模拟容器行为，在本地目录拉取代码，执行pytest，生成测试报告。&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/3160ec51a9094badbf1543a35568a028.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;容器模式：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8cea72d575a34a86a29e6d21f9d792e4.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;本地模式：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/1d2d7ccffec842b58332cf3a5ce6ffc8.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;平台大改造&#34;&gt;平台大改造&lt;/h1&gt;
&lt;p&gt;pytestx平台更轻、更薄，移除了用例管理、任务关联用例相关功能代码，只保留真正的任务调度功能，backend的requirements.txt解耦，只保留后端依赖，pytest相关依赖转移到tep-project。&lt;/p&gt;
&lt;p&gt;那如何管理用例呢？约定大于配置，&lt;strong&gt;我们约定pytest项目已经通过目录维护好了一个稳定的自动化用例集&lt;/strong&gt;，也就是说需要通过平台任务调度的用例，都统一存放在目录X下，这些用例基本不需要维护，可以每日稳定执行，然后将目录X配置到平台任务信息中，按指定目录执行用例集。对于那些不够稳定的用例，就不能放到目录X下，需要调试好以后再纳入。&lt;/p&gt;
&lt;p&gt;为什么不用marker？pytest的marker确实可以给测试用例打标记，也有人是手动建立任务和用例进行映射，这些方式都不如维护一个稳定的自动化用例集方便，在我们公司平台上，也是维护用例集，作为基础用例集。使用pytest项目同理。&lt;/p&gt;
&lt;h1 id=&#34;核心代码&#34;&gt;核心代码&lt;/h1&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/a1cce43d2acb43cf801fd58a5a60c36d.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5a392cdc2b8b4f04bf1e267f16bffff2.png&#34;&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;&lt;span style=&#34;color:#57606a&#34;&gt;#!/bin/bash
&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:#953800&#34;&gt;PkgName&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;backend&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#953800&#34;&gt;Dockerfile&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;./deploy/Dockerfile.backend&amp;#39;&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:#953800&#34;&gt;DockerContext&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;./
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#6639ba&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Start build image...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build -f &lt;span style=&#34;color:#953800&#34;&gt;$Dockerfile&lt;/span&gt; -t &lt;span style=&#34;color:#953800&#34;&gt;$PkgName&lt;/span&gt; &lt;span style=&#34;color:#953800&#34;&gt;$DockerContext&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:#cf222e&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#953800&#34;&gt;$?&lt;/span&gt; -eq &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#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:#cf222e&#34;&gt;then&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:#6639ba&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Build docker image success&amp;#34;&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:#6639ba&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Start run image...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    docker run -p 8000:80 &lt;span style=&#34;color:#953800&#34;&gt;$PkgName&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:#cf222e&#34;&gt;else&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:#6639ba&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Build docker image failed&amp;#34;&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:#cf222e&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;python:3.8&lt;/span&gt;&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;ENV&lt;/span&gt; LANG C.UTF-8&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;ENV&lt;/span&gt; &lt;span style=&#34;color:#953800&#34;&gt;TZ&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;Asia/Shanghai&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;RUN&lt;/span&gt; /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;Asia/Shanghai&amp;#39;&lt;/span&gt; &amp;gt;/etc/timezone&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;WORKDIR&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;/app&lt;/span&gt;&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;COPY&lt;/span&gt; ./backend .&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;RUN&lt;/span&gt; pip install -r ./requirements.txt -i &lt;span style=&#34;color:#0a3069&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    https://pypi.tuna.tsinghua.edu.cn/simple &lt;span style=&#34;color:#0a3069&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    --default-timeout&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;3000&lt;/span&gt;&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#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:#f6f8fa;background-color:#82071e&#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:#cf222e&#34;&gt;CMD&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;python&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;./manage.py&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;runserver&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;0.0.0.0:80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#f6f8fa;background-color:#82071e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;数据库表&lt;/strong&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>开源测试平台开发期待你的加入</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/japi/000003-%E5%BC%80%E6%BA%90%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E6%9C%9F%E5%BE%85%E4%BD%A0%E7%9A%84%E5%8A%A0%E5%85%A5/</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/japi/000003-%E5%BC%80%E6%BA%90%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E6%9C%9F%E5%BE%85%E4%BD%A0%E7%9A%84%E5%8A%A0%E5%85%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;在2022年7月6日我曾组织过一次开源测试平台开发报名，有50+人报名，到2023年1月28日项目暂停，如大家在Gittee看到的，除我以外，只有1个人真正提交了代码：奉孝。&lt;/p&gt;
&lt;p&gt;原因如其中一位同学所说：“感觉我们大部份还是缺少从0到1的经验，负责的内容有时候不要知道从哪下手，又少了点积极性”。描述得很贴切，可能刚开始动力很足，但因为缺少经验，不够积极，最后实践不了。说的很好，就是做不到。也很无奈。&lt;/p&gt;
&lt;p&gt;二次启动，分为2个阶段，第一个阶段是学习阶段，我带着大家一起把teprunner平台从0到1开发一遍，把需要掌握的知识先学一遍，保证有一定基础，挑战能够独立完成模块开发。第二个阶段是开发阶段，共同完成EasyPytest测试平台开发，学以致用，加深对前后端分离项目开发的理解，具有真实的平台开发实践。&lt;/p&gt;
&lt;h1 id=&#34;学习阶段&#34;&gt;学习阶段&lt;/h1&gt;
&lt;p&gt;你都不维护了，我为什么还要学teprunner？Django没意思？我想学FastAPI？&lt;/p&gt;
&lt;p&gt;teprunner有一套完整教程，包括前后端源码、11篇电子书教程、2小时视频讲解，虽然我因为工作原因没有继续维护teprunner，但是它仍然非常适合初学者入门学习测试开发，掌握里面的知识，对于后续学习其他技术，都有很大帮助。我的技术提升除了二刷《流畅的Python》和编写了tep开源工具，最大的提升就是完成了teprunner测试平台开发，如果你也能自己动手完成一遍，一定有极大提升。&lt;/p&gt;
&lt;p&gt;Django、Flask、FastAPI、SpringBoot，我认为从入门者角度来说，Django是最适合的，它属于开箱即用的框架，很多东西都给你准备好了，你用就行，避免刚开始就陷入环境搭建，三方库拼装、数据库维护的麻烦中，降低理解门槛和操作难度。学会了Django，再学其他的，也很简单，一通百通。刚开始学习，学哪个不是最重要的，最重要的是先学会一个，迈出第一步。以我的经验来说，学习成熟框架Django是个正确选择。&lt;/p&gt;
&lt;h1 id=&#34;开发阶段&#34;&gt;开发阶段&lt;/h1&gt;
&lt;p&gt;EasyPytest的后端把Django切换为了FastAPI，掌握了Django，再对比FastAPI，就能对后端开发框架有更深入的理解。就像我学Python和Java一样，只学一门百思不得其解，对比一看，豁然开朗。目前已经完成了登录、首页、用户管理的开发，Gitee也有相应源码。未来的规划，是做teprunner的升级版，实现pytest平台化，80%工作本地开发脚本，20%工作交给平台编排运行。&lt;/p&gt;
&lt;h1 id=&#34;报名方式&#34;&gt;报名方式&lt;/h1&gt;
&lt;p&gt;添加微信&lt;strong&gt;cekaigang&lt;/strong&gt;，备注参加平台开发，进群。&lt;/p&gt;
&lt;p&gt;现开启&lt;strong&gt;学习阶段&lt;/strong&gt;报名：&lt;/p&gt;
&lt;p&gt;按需选择，10元/节，录播+直播集中答疑。报名后会获得一份录播+一次集中答疑直播+一个每节课源码。每节课源码跟GitHub上源码不同，只会包含当前这节课进度的源码，一步一步的看到测试平台是如何开发出来的。&lt;/p&gt;
&lt;p&gt;课表链接： &lt;a href=&#34;https://eqgvpqzl6c.feishu.cn/mindnotes/bmncndM8oei2B0MIFE1RMrGuKjd&#34;&gt;https://eqgvpqzl6c.feishu.cn/mindnotes/bmncndM8oei2B0MIFE1RMrGuKjd&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;开发阶段报名需至少参与一节学习课程，从优秀学员或积极学员中选择，时间另行通知。&lt;/p&gt;
&lt;p&gt;当然，如果你已经具备奉孝这样的技术能力和积极性，欢迎直接与我联系，请提供你的开源项目地址或在Gitee提交Pull Requests，以证明能够独立完成模块开发，直接参与EP平台共建。&lt;/p&gt;</description>
    </item>
    <item>
      <title>框架V2.0.0开发指南</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000003-%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6%E6%A1%86%E6%9E%B6v2.0.0%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/</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/tep/000003-%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6%E6%A1%86%E6%9E%B6v2.0.0%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;strong&gt;温馨提示：开发指南，根据V2.0.0编写，不随版本更新维护，仅供入门参考。最新实现请结合教程和源码阅读，教程会在版本发布后维护。&lt;/strong&gt;</description>
    </item>
    <item>
      <title>一次全量数据对比工具发现问题的过程与思考</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/%E5%B0%8F%E5%B7%A5%E5%85%B7/000003-%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E4%B8%80%E6%AC%A1%E5%85%A8%E9%87%8F%E6%95%B0%E6%8D%AE%E5%AF%B9%E6%AF%94%E5%B7%A5%E5%85%B7%E5%8F%91%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8E%E6%80%9D%E8%80%83/</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/%E5%B0%8F%E5%B7%A5%E5%85%B7/000003-%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E4%B8%80%E6%AC%A1%E5%85%A8%E9%87%8F%E6%95%B0%E6%8D%AE%E5%AF%B9%E6%AF%94%E5%B7%A5%E5%85%B7%E5%8F%91%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E8%BF%87%E7%A8%8B%E4%B8%8E%E6%80%9D%E8%80%83/</guid>
      <description>如果没有这次全量数据对比工具，那么也许这个历史问题会继续隐藏着，直到发生线上事故才暴露出来，毕竟人工抽样验证发现的概率只有&lt;strong&gt;5.8%&lt;/strong&gt;。</description>
    </item>
    <item>
      <title>pytestx重新定义接口框架设计</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/pytestx/000002-pytestx%E9%87%8D%E6%96%B0%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1/</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/pytestx/000002-pytestx%E9%87%8D%E6%96%B0%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%8F%A3%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1/</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;脚手架：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/aefec433fde14d39abde74206ab83341.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;目录：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d271b2542dab494595723669cc50dd81.png&#34;&gt;&lt;/p&gt;
&lt;p&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-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&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;测试登录到下单流程，需要先启动后端服务
&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;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test_data &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;查询SKU&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;skuName&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;电子书&amp;#34;&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:#0a3069&#34;&gt;&amp;#34;添加购物车&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;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;123&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;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;2&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:#0a3069&#34;&gt;&amp;#34;下单&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;orderId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;222&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;payAmount&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;0.2&amp;#34;&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:#0a3069&#34;&gt;&amp;#34;支付&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;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;123&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;price&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;0.1&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;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;2&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;totalPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;0.2&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;case_vars &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;dict&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#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:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;http&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; login_headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; file_data&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:#57606a&#34;&gt;# 搜索商品&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; file_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;/api/tasks/mock/searchSku&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; test_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;查询SKU&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; http&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;login_headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; params&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.skuId&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;    case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.price&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# 添加购物车&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; file_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;/api/tasks/mock/addCart&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; test_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;添加购物车&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; http&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;login_headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.skuNum&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;    case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;totalPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.totalPrice&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# 下单&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; file_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;/api/tasks/mock/order&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; test_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;下单&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuId&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuPrice&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuNum&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;skuNum&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;totalPrice&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;totalPrice&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; http&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;login_headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;orderId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.orderId&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# 支付&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; file_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;domain&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;/api/tasks/mock/pay&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; test_data&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;支付&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;    body&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;orderId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; case_vars&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;orderId&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    response &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; http&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;url&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;login_headers&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;body&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;400&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:#cf222e&#34;&gt;assert&lt;/span&gt; response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;jsonpath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;$.success&amp;#34;&lt;/span&gt;&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:#0a3069&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;页面下载脚手架&#34;&gt;页面下载脚手架&lt;/h1&gt;
&lt;p&gt;启动平台前后端服务后，从页面下载脚手架，平台会拉取开源项目tep-project最新代码，打成压缩包，生成下载文件，弹窗下载。&lt;/p&gt;</description>
    </item>
    <item>
      <title>tep用户手册帮你从unittest过渡到pytest</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000002-tep%E7%94%A8%E6%88%B7%E6%89%8B%E5%86%8C%E5%B8%AE%E4%BD%A0%E4%BB%8Eunittest%E8%BF%87%E6%B8%A1%E5%88%B0pytest/</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/tep/000002-tep%E7%94%A8%E6%88%B7%E6%89%8B%E5%86%8C%E5%B8%AE%E4%BD%A0%E4%BB%8Eunittest%E8%BF%87%E6%B8%A1%E5%88%B0pytest/</guid>
      <description>unittest和pytest是Python的2个强大的测试框架，经常用来做UI自动化或接口自动化。unittest是PyCharm的默认集成工具，也是我们大多数人入门自动化的首选框架。pytest提供了更丰富的功能，相对的上手难度也要高一点。如果学了pytest后，想快速用pytest写项目，用于工作实践，那么可以试试我写的这款测试工具：&lt;code&gt;tep&lt;/code&gt;，try easy pytest。</description>
    </item>
    <item>
      <title>功能测试用例自动生成算法Pairwise工具</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/%E5%B0%8F%E5%B7%A5%E5%85%B7/000002-%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E7%AE%97%E6%B3%95pairwise%E5%B7%A5%E5%85%B7/</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/%E5%B0%8F%E5%B7%A5%E5%85%B7/000002-%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E7%AE%97%E6%B3%95pairwise%E5%B7%A5%E5%85%B7/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;pairwise算法是什么&#34;&gt;Pairwise算法是什么&lt;/h2&gt;
&lt;p&gt;对于以下测试场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器：M，O，P&lt;/li&gt;
&lt;li&gt;操作平台：W（windows），L（linux），i（ios）&lt;/li&gt;
&lt;li&gt;语言：C（chinese），E（english）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;该如何设计功能测试用例呢？&lt;/p&gt;
&lt;p&gt;根据数学统计分析，73%的缺陷（单因子是35%，双因子是38%）是由单因子或2个因子相互作用产生的。19%的缺陷是由3个因子相互作用产生的。也就是说，&lt;strong&gt;大多数的bug都是条件的两两组合造成的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Pairwise算法是L. L. Thurstone在1927年首先提出来的，他是美国的一位心理统计学家。Pairwise算法基于两两组合，过滤出性价比高的用例集。它的思路是：&lt;strong&gt;如果某一组用例的两两组合结果，在其他组合中均出现，就删除该组用例，从而精简用例&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;对于上述测试场景，可以通过笛卡尔积设计18条两两组合的测试用例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1，M W C
2，M W E
3，M L C
4，M L E
5，M I C
6，M I E
7，O W C
8，O W E
9，O L C
10，O L E
11，O I C
12，O I E
13，P W C
14，P W E
15，P L C
16，P L E
17，P I C
18，P I E
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;对于第18条用例&lt;code&gt;P I E&lt;/code&gt;来说，两两组合是&lt;code&gt;PI ，PE ，IE&lt;/code&gt;，&lt;code&gt;PI&lt;/code&gt;在17号，&lt;code&gt;PE&lt;/code&gt;在16号，&lt;code&gt;IE&lt;/code&gt;在12号出现过，所以第18条用例可以过滤掉。按照这个算法继续过滤，最终剩下9条用例：&lt;/p&gt;</description>
    </item>
    <item>
      <title>坚持开发原创测试平台</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/japi/000002-%E5%9D%9A%E6%8C%81%E5%BC%80%E5%8F%91%E5%8E%9F%E5%88%9B%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0/</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/japi/000002-%E5%9D%9A%E6%8C%81%E5%BC%80%E5%8F%91%E5%8E%9F%E5%88%9B%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0/</guid>
      <description>&lt;strong&gt;不管测试平台有没有实际用处，具备测试平台开发能力，是测试开发工程师的必备技能之一。&lt;/strong&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/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>EasyPytest测试平台开发日志之系统设计</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/easypytest/000001-easypytest%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E6%97%A5%E5%BF%97%E4%B9%8B%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</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/easypytest/000001-easypytest%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E6%97%A5%E5%BF%97%E4%B9%8B%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</guid>
      <description>&lt;strong&gt;EasyPytest测试平台，帮你高效管理Pytest测试用例。&lt;/strong&gt;</description>
    </item>
    <item>
      <title>japi项目需求分析阶段</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/japi/000001-japi%E9%A1%B9%E7%9B%AE%E9%9C%80%E6%B1%82%E5%88%86%E6%9E%90%E9%98%B6%E6%AE%B5/</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/japi/000001-japi%E9%A1%B9%E7%9B%AE%E9%9C%80%E6%B1%82%E5%88%86%E6%9E%90%E9%98%B6%E6%AE%B5/</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;&lt;strong&gt;japi&lt;/strong&gt;项目，是一个接口自动化平台开发项目，由【交流群】招人共建，基于JMeter，参考MeterSphere，以学习为目的，完成一个SpringBoot+Vue技术栈的纯粹的接口自动化平台开发。&lt;/p&gt;
&lt;h1 id=&#34;项目成员&#34;&gt;项目成员&lt;/h1&gt;
&lt;p&gt;经过&amp;quot;填写人员分工表&amp;quot;以后，最终有38人参与了本次项目：&lt;/p&gt;
&lt;p&gt;刚哥、布鲁、嘉宥、守正、大刚、风雨、奉孝、大姚、畅月、渔夫、小禹、钰琪、天湖、迷龙、肥涵、胖子、局长、呼呼、明亮、安宁、木棉、番茄、玥玥、哈瑞、虚竹、十五、阿新、卡罗、大白、库昊、左右、小涌、古风、玉洁、小韩、小卷、千凝、昆明。&lt;/p&gt;
&lt;p&gt;随着项目进行，后续“提交学习总结”等环节层层递进，真正留下来的项目成员，会在GitHub的README中署名。&lt;/p&gt;
&lt;p&gt;在填表时收集了大家的历史经验，这里汇总整理了一下，供参考：&lt;/p&gt;
&lt;p&gt;“Vue+Django写过测试平台；SpringBoot写过后端应用；Python小工具开发；Python+Pytest+Requests搭建接口自动化框架；Python+Unittest或Pytest自动化测试框架搭建及脚本开发；各类开源自动化测试平台搭建，如：HttpRunnerManager、流马、Yapi、Sonic等；js+Django2实现UI自动化测试平台；使Testng框架进行接口测试；Vue+fastapi写过测试平台；python+selenium UI自动化；python+httprunner，接口文档解析自动生成字段校验用例；python+RobotFramework做过UI；Java做过单元测试，熟悉基础语法；学过JavaSE，JavaEE，实战经验不多；Python Pyqt GUI工具开发；Python 接口自动化、APP自动化框架设计及编码；独立负责主导Python+Locust性能测试；三方库和标准库：unittest、pytest、appium、selenium、request、httprunner；画过简单的html、css；App UI自动化框架搭建：pytest+appium+allure+jenkins接口自动化框架搭建：pytest+request+allure+jenkins二次开发及维护过flask+vue接口测试平台；二次开发及维护过springboot+angularUI测试平台；Pytest+Playwright UI自动化脚本编写；”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我总结了几个关键字：Vue、Django、SpringBoot、Flask、FastAPI、ElementUI、RobotFramework、脚本开发、工具开发、平台开发、接口自动化、UI自动化、APP自动化。&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;/p&gt;
&lt;h1 id=&#34;需求分析&#34;&gt;需求分析&lt;/h1&gt;
&lt;p&gt;MeterSphere官方给出的功能列表，接口自动化相关的模块有接口定义和接口自动化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;接口定义包括以下功能：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在线添加并编辑接口定义;
以树状形式展示项目的模块及对应接口;
支持HTTP、Dubbo、SQL、TCP类型接口请求;
支持通过MeterSphere Json、Postman、Swagger 等多种格式文件快速导入接口，Swagger可通过URL定时同步;
支持接口快捷调试;
根据接口定义快速配置HTTP、TCP协议Mock服务;
API列表、CASE列表、文档模式多种模式灵活切换;
接口页面API、TEST、CASE、MOCK多种操作灵活切换;
测试环境信息管理功能;
单接口用例支持引用环境;
支持正则匹配、jsonpath、xpath等多种类型的断言规则;
支持正则匹配、jsonpath、xpath等多种类型的参数提取;
支持BeanShell、Python、Groovy、JavaScript等多种语言的前后置脚本;
支持前置SQL、后置SQL;
支持上传并引用自定义Jar包;
支持查看接口及用例变更历史;
手动执行、定时任务、Jenkins插件触发等多种执行方式;
API、CASE一键复制;
CASE一键创建性能测试;
接口定义依赖关系设置;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;接口自动化包括以下功能：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创建多接口串联的场景化测试;
从接口列表中快速导入接口或接口用例;
跨项目的场景引用功能;
客户端SSL证书认证配置;
场景用例支持引用环境;
支持条件、循环、事务、等待等多种类型的控制器;
支持正则匹配、jsonpath、xpath等多种类型的断言规则;
支持正则匹配、jsonpath、xpath等多种类型的参数提取;
支持BeanShell、Python、Groovy、JavaScript等多种语言的前后置脚本;
场景变量提供多种类型;
提供Mock数据和JMeter变量表达式;
环境配置增加区分模块、路径两种方式添加域名;
增加全局前置脚本、全局后置脚本;
基于插件机制实现其他协议;
支持上传并引用自定义Jar包;
支持查看场景用例变更历史;
手动执行、定时任务、Jenkins插件触发等多种执行方式;
串行、并行两种批量执行方式自由选择;
支持指定测试资源池远程执行;
可多场景串行、并行执行;
场景一键创建性能测试;
场景一键复制;
场景批量执行可自由选择独立报告或集合报告;
场景一键添加到测试计划；&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;除此之外还有：&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>JSON转SQL小工具源码分享</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/%E5%B0%8F%E5%B7%A5%E5%85%B7/000001-json%E8%BD%ACsql%E5%B0%8F%E5%B7%A5%E5%85%B7%E6%BA%90%E7%A0%81%E5%88%86%E4%BA%AB/</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/%E5%B0%8F%E5%B7%A5%E5%85%B7/000001-json%E8%BD%ACsql%E5%B0%8F%E5%B7%A5%E5%85%B7%E6%BA%90%E7%A0%81%E5%88%86%E4%BA%AB/</guid>
      <description>将key-value的JSON数据，转换为insert的SQL语句，进而实现网页数据存储到数据库。</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>
    <item>
      <title>tep首次开源V0.2.3介绍</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/tep/000001-tep%E9%A6%96%E6%AC%A1%E5%BC%80%E6%BA%90v0.2.3%E4%BB%8B%E7%BB%8D/</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/tep/000001-tep%E9%A6%96%E6%AC%A1%E5%BC%80%E6%BA%90v0.2.3%E4%BB%8B%E7%BB%8D/</guid>
      <description>『  tep is a testing tool to help you write pytest more easily. Try Easy Pytest!  』</description>
    </item>
    <item>
      <title>聚焦于任务调度的测试平台pytestx</title>
      <link>https://dongfanger.github.io/posts/%E5%8E%9F%E5%88%9B%E9%A1%B9%E7%9B%AE/pytestx/000001-%E8%81%9A%E7%84%A6%E4%BA%8E%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6%E7%9A%84%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0pytestx/</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/pytestx/000001-%E8%81%9A%E7%84%A6%E4%BA%8E%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6%E7%9A%84%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0pytestx/</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;&lt;strong&gt;聚焦于任务调度&lt;/strong&gt;，接口自动化80%本地编写，20%交由平台管理。&lt;/p&gt;
&lt;p&gt;如果使用pytest做接口自动化，那么个人认为最好的编写工具是PyCharm，任何低代码测试平台都无法取代。当然不会代码，或者不使用pytest，那低代码测试平台，或者yaml，甚至excel写自动化用例，都是可以接受的。而在使用pytest这个特定场景里面，要做平台化，平台的功能就需要仔细斟酌。既然编写用例最好使用PyCharm，平台也就只能专注于用例编排和任务调度，即创建任务，关联用例，批量运行，以及定时，并行，通知等。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pytestx&lt;/strong&gt;正是基于此理念设计的一款纯粹的任务调度平台。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/883d930149b2437da1b3f410b1835ffa.png&#34;&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/82056831880343ad8a090709ee6abd26.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;tep100的延续&#34;&gt;tep1.0.0的延续&lt;/h1&gt;
&lt;p&gt;tep1.0.0版本已经封版，在pytestx中对它进行了延续。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;从pypi到git&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;tep是上传到pypi的，通过pip命令安装，使用tep startproject命令创建项目脚手架，代码结构分为项目代码和tep代码。改造后的tep只有项目代码，存放在：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gitee.com/dongfanger/tep-project.git&#34;&gt;https://gitee.com/dongfanger/tep-project.git&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;也就是说，在这个项目中，能看到整个tep全貌。之前的tep内核代码，比如fixtures自动寻找、内置fixture、环境变量等，全部迁移到了项目代码里面，尤其是conftest.py的文件内容。pip install tep顺带安装的依赖包，也整理到了requirements.txt，按照说明安装即可。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pytest-html替换allure&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由重到轻，化繁为简，pytest-html能够满足数据+日志的报告需要，最新的V3.2.0版本的界面尚可。诚然allure漂亮，但安装Java、下载Allure、配置环境变量、安装pytest-allure、添加pytest hook还是相对麻烦。更重要的是，要支持平台在线查看报告，减少存储占用，pytest-html无疑是更好的选择。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;下载脚手架&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;既可以从使用git clone下载代码，也可以从页面下载，就像spring.io那样：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/78d828f0b74c457a9a610ff093891909.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fastapi到django&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;之前的示例代码是调用的fastapi启动的应用，pytestx则将示例代码的接口换成了Django自身的mock接口。&lt;/p&gt;
&lt;h1 id=&#34;teprunner的延续&#34;&gt;teprunner的延续&lt;/h1&gt;
&lt;p&gt;pytestx的基础代码是从teprunner来的，对数据库设计、代码规范、项目结构做了优化。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/89f0add05f4145f586844c02be511aef.png&#34;&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/f22f47846fc5419284141f72f7259d22.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一、同步项目&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;同步项目，是把项目配置的Git仓库地址和分支，更新到本地沙箱中，项目之间完全隔离。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5422dbfe859f44cf8127e852542d1d17.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;并把用例落库：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/481995032b754a7baed495bf29f3cd10.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二、任务关联用例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创建任务，任务关联用例，就形成了新的用例集。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三、容器化运行&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当前是假容器，未真正引入Docker，所谓容器，是指每次运行，都将用例拉取到新的目录，批量执行：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/711257c91504476f8679320e277c53e7.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;四、生成报告&lt;/p&gt;
&lt;p&gt;pytest命令生成html报告，存放于reports，借助于Django挂载页面，从页面点击，在线查看报告：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/bee4ca83f58a4fad98d3814f98d07729.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;体验平台&#34;&gt;体验平台&lt;/h1&gt;
&lt;p&gt;下载源码：https://github.com/dongfanger/pytestx&lt;/p&gt;
&lt;p&gt;安装依赖：前端（npm install）后端（pip install -r requirements.txt）&lt;/p&gt;
&lt;p&gt;启动服务：前端（npm run serve）后端（python manage.py runserver）&lt;/p&gt;
&lt;p&gt;即可畅快体验。&lt;/p&gt;
&lt;h1 id=&#34;学习交流&#34;&gt;学习交流&lt;/h1&gt;
&lt;p&gt;添加微信cekaigang，加入交流群，欢快讨论吧。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/67f63522e3fb431485eea9bb21983888.png&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/dongfanger/pytestx&#34;&gt;https://github.com/dongfanger/pytestx&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
    </item>
  </channel>
</rss>
