<?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/%E6%B5%8B%E8%AF%95/</link>
    <description>Recent content in 测试 on 测试开发刚哥</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 21 Jan 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://dongfanger.github.io/categories/%E6%B5%8B%E8%AF%95/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>单篇长文 TestNG 从入门到精通</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E5%8D%95%E7%AF%87%E9%95%BF%E6%96%87testng%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/</link>
      <pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E5%8D%95%E7%AF%87%E9%95%BF%E6%96%87testng%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/</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;TestNG 是 Test Next Generation 的缩写，它的灵感来自于 JUnit 和 NUnit，在它们基础上增加了很多很牛的功能，比如说：&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;li&gt;支持数据驱动（@DataProvider）。&lt;/li&gt;
&lt;li&gt;支持参数化。&lt;/li&gt;
&lt;li&gt;强大的执行机制（不需要 TestSuite）。&lt;/li&gt;
&lt;li&gt;能跟各种工具结合（比如 IDEA、Maven 等）。&lt;/li&gt;
&lt;li&gt;内嵌 BeanShell。&lt;/li&gt;
&lt;li&gt;提供运行时和日志的 JDK 函数（不需要添加 dependencies）。&lt;/li&gt;
&lt;li&gt;提供应用服务器测试依赖的方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话总结就是，&lt;strong&gt;TestNG 强大到能覆盖所有测试类型：单元测试、功能测试、端到端测试、集成测试&lt;/strong&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;package&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;example1&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.testng.annotations.*&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;SimpleTest&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@BeforeClass&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;setUp&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;   &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// code that will be invoked when this test is instantiated&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;groups&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;fast&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;aFastTest&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;   &lt;/span&gt;System&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;out&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;println&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Fast test&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;groups&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;slow&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;aSlowTest&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;System&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;out&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;println&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Slow test&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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 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;ul&gt;
&lt;li&gt;setUp()会在&lt;strong&gt;测试类创建后，测试方法执行前&lt;/strong&gt;运行。&lt;/li&gt;
&lt;li&gt;测试方法名字可以任意的，TestNG 通过@Test 注解来标识。&lt;/li&gt;
&lt;li&gt;可以把测试方法按 group 分组。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后使用 xml 来配置运行策略：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Allure&#43;Maven&#43;JUnit5 配置实践</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/allure&#43;maven&#43;junit5%E9%85%8D%E7%BD%AE%E5%AE%9E%E8%B7%B5/</link>
      <pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/allure&#43;maven&#43;junit5%E9%85%8D%E7%BD%AE%E5%AE%9E%E8%B7%B5/</guid>
      <description>&lt;strong&gt;为什么要用 JUnit5？&lt;/strong&gt;</description>
    </item>
    <item>
      <title>Locust 性能测试工具核心技术@task 和@events</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/locust%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF@task%E5%92%8C@events/</link>
      <pubDate>Sun, 18 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/locust%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF@task%E5%92%8C@events/</guid>
      <description>Tasks 和 Events 是 Locust 性能测试工具的核心技术，有了它们，Locust 才能称得上是一个性能工具。</description>
    </item>
    <item>
      <title>locustfile 中的 User 类和 HttpUser 类</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/locustfile%E4%B8%AD%E7%9A%84user%E7%B1%BB%E5%92%8Chttpuser%E7%B1%BB/</link>
      <pubDate>Sat, 17 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/locustfile%E4%B8%AD%E7%9A%84user%E7%B1%BB%E5%92%8Chttpuser%E7%B1%BB/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;locustfile-是什么&#34;&gt;locustfile 是什么？&lt;/h1&gt;
&lt;p&gt;locustfile 是 Locust 性能测试工具的用户脚本，描述了单个用户的行为。&lt;/p&gt;
&lt;p&gt;locustfile 是个普通的 Python 模块，如果写作&lt;code&gt;locustfile.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-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ locust
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果换个名字，那么只能通过&lt;code&gt;-f&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;$ locust -f locust_files/my_locust_file.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;与一般 Python 模块不同的是：&lt;strong&gt;locustfile 必须至少定义一个类，且继承自 User 类。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;user-类&#34;&gt;User 类&lt;/h1&gt;
&lt;p&gt;User 类表示性能测试的模拟用户，&lt;strong&gt;Locust 会在运行时创建 User 类的实例&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;wait_time-属性&#34;&gt;wait_time 属性&lt;/h2&gt;
&lt;p&gt;设置等待时间，默认值不等待，立即执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Locust 支持 4 种方式设置 wait_time 属性&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为了便于理解实际意义，我把源码贴在了下面。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;constant&lt;/code&gt;函数，常量，任务执行完毕等待 X 秒开始下一任务。&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;constant&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;wait_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;    &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;    Returns a function that just returns the number specified by the wait_time argument
&lt;/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;    Example::
&lt;/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;        class MyUser(User):
&lt;/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;            wait_time = constant(3)
&lt;/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:#cf222e&#34;&gt;lambda&lt;/span&gt; instance&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; wait_time
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;between&lt;/code&gt;函数，区间随机值，任务执行完毕等待 X-Y 秒（中间随机取值）开始下一任务。&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;between&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;min_wait&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; max_wait&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;    Returns a function that will return a random number between min_wait and max_wait.
&lt;/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;    Example::
&lt;/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;        class MyUser(User):
&lt;/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;            # wait between 3.0 and 10.5 seconds after each task
&lt;/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;            wait_time = between(3.0, 10.5)
&lt;/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:#cf222e&#34;&gt;lambda&lt;/span&gt; instance&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; min_wait &lt;span style=&#34;color:#0550ae&#34;&gt;+&lt;/span&gt; random&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;random&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;max_wait &lt;span style=&#34;color:#0550ae&#34;&gt;-&lt;/span&gt; min_wait&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;constant_pacing&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;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;constant_pacing&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;wait_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;    &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;    Returns a function that will track the run time of the tasks, and for each time it&amp;#39;s
&lt;/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;    called it will return a wait time that will try to make the total time between task
&lt;/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;    execution equal to the time specified by the wait_time argument.
&lt;/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;    In the following example the task will always be executed once every second, no matter
&lt;/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;    the task execution 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:#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;        class MyUser(User):
&lt;/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;            wait_time = constant_pacing(1)
&lt;/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;            @task
&lt;/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 my_task(self):
&lt;/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;                time.sleep(random.random())
&lt;/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;    If a task execution exceeds the specified wait_time, the wait will be 0 before starting
&lt;/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;    the next task.
&lt;/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 style=&#34;color:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;wait_time_func&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;
&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:#6639ba&#34;&gt;hasattr&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; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;_cp_last_run&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:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;_cp_last_wait_time &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; wait_time
&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;_cp_last_run &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;            &lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt; wait_time
&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;            run_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 style=&#34;color:#0550ae&#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;_cp_last_run &lt;span style=&#34;color:#0550ae&#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;_cp_last_wait_time
&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;_cp_last_wait_time &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;max&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; wait_time &lt;span style=&#34;color:#0550ae&#34;&gt;-&lt;/span&gt; run_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;            &lt;span style=&#34;color:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;_cp_last_run &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;            &lt;span style=&#34;color:#cf222e&#34;&gt;return&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;_cp_last_wait_time
&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; wait_time_func
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自定义&lt;code&gt;wait_time&lt;/code&gt;方法，比如每次等待时间 1 秒 2 秒 3 秒递增：&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;class&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;MyUser&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;User&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;    last_wait_time &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;0&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;wait_time&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;
&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;last_wait_time &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&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:#6a737d&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;last_wait_time
&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;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>Python 技术栈性能测试工具 Locust 入门</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/python%E6%8A%80%E6%9C%AF%E6%A0%88%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7locust%E5%85%A5%E9%97%A8/</link>
      <pubDate>Fri, 16 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/python%E6%8A%80%E6%9C%AF%E6%A0%88%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7locust%E5%85%A5%E9%97%A8/</guid>
      <description>Locust 是一款 Python 技术栈的开源的性能测试工具。Locust 直译为蝗虫，寓意着它能产生蝗虫般成千上万的并发用户：</description>
    </item>
    <item>
      <title>性能测试指标 TP99，比平均响应时间更需要关注</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8C%87%E6%A0%87tp99%E6%AF%94%E5%B9%B3%E5%9D%87%E5%93%8D%E5%BA%94%E6%97%B6%E9%97%B4%E6%9B%B4%E9%9C%80%E8%A6%81%E5%85%B3%E6%B3%A8/</link>
      <pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E6%8C%87%E6%A0%87tp99%E6%AF%94%E5%B9%B3%E5%9D%87%E5%93%8D%E5%BA%94%E6%97%B6%E9%97%B4%E6%9B%B4%E9%9C%80%E8%A6%81%E5%85%B3%E6%B3%A8/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;什么是-tp99&#34;&gt;什么是 TP99？&lt;/h1&gt;
&lt;p&gt;TP 是 Top Percentile 的首字母缩写，TP99 是指“前百分之九十九”，用来表示满足百分之九十九的网络请求所需的最低耗时。&lt;/p&gt;
&lt;p&gt;该怎么理解？举个简单例子，下图是 100 次请求的响应时间走势图：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/e47a6fabaec14c8ebef5d085f0fa0399.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;第 23 次请求的响应时间为 600ms，其他请求的响应全部为 1ms，这个走势图也就是常说的“尖刺”。我们来计算一下：&lt;/p&gt;
&lt;p&gt;①平均响应时间&lt;/p&gt;
&lt;p&gt;平均响应时间 = 所有请求响应时间之和 / 请求次数，等于 6.99ms&lt;/p&gt;
&lt;p&gt;②TP99&lt;/p&gt;
&lt;p&gt;TP99 = 把响应时间从小到大排列，取第 99%那个位置的值，等于 1ms&lt;/p&gt;
&lt;h1 id=&#34;为什么-tp99-更需要关注&#34;&gt;为什么 TP99 更需要关注？&lt;/h1&gt;
&lt;p&gt;平均响应时间受极端值的影响较大，因为出现 1 个尖刺，平均响应时间几乎是 TP99 的 7 倍！&lt;/p&gt;
&lt;p&gt;TP99 更能反映大多数请求的情况，通过 TP99 为 1ms 可以知道，应用系统对于大多数请求，都能在 1ms 内响应，整体性能达标。而对于尖刺，我们可以通过走势图的 MAX 来发现，单独进行分析。&lt;/p&gt;
&lt;p&gt;在 JMeter 的 Aggregate Report 中，有 3 个字段：90% Line、95% Line、99%Line&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/de01b3cdbe37490f8dff7a9d105d28b5.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;其实就是指 TP90、TP95、TP99。此外，在实际工作中，我们还会关注 TP999，三个九，也就是 99.9%。关注 TP 多少，取决于我们对性能的要求怎么样，我们希望应用系统满足多少请求。如果对性能要求不高，关注 TP90 就够了，允许 10%请求出现波动。如果对性能要求很高，则需要关注 TP999，99.9%请求都能够快速响应。&lt;/p&gt;</description>
    </item>
    <item>
      <title>你的下一个压测工具可以是 nGrinder</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BD%A0%E7%9A%84%E4%B8%8B%E4%B8%80%E4%B8%AA%E5%8E%8B%E6%B5%8B%E5%B7%A5%E5%85%B7%E5%8F%AF%E4%BB%A5%E6%98%AFngrinder/</link>
      <pubDate>Wed, 14 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BD%A0%E7%9A%84%E4%B8%8B%E4%B8%80%E4%B8%AA%E5%8E%8B%E6%B5%8B%E5%B7%A5%E5%85%B7%E5%8F%AF%E4%BB%A5%E6%98%AFngrinder/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;ngrinder-小众而实用&#34;&gt;nGrinder 小众而实用&lt;/h1&gt;
&lt;p&gt;nGrinder 在 GitHub 的 star 有 1.6k，相比于 JMeter6.4k 来说，要少很多。在测试资料中，关于压测工具也是以 JMeter 居多，几乎没有专门针对 nGrinder 的学习资料。nGrinder 既然这么小众，那为什么还要学它呢？因为听说咱们&lt;strong&gt;公司的压测平台，底层就是用的 nGrinder&lt;/strong&gt;，对于企业级应用来说，nGrinder 还是很实用的，正如它的介绍：“Enterprise level performance testing solution based on The Grinder”。&lt;/p&gt;
&lt;p&gt;nGrinder 有以下特点：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/1cc334dacd5b43a18998748c60aaeccd.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;我们编写压测脚本用到的就是第一个特点提到的 Groovy 脚本，跟 Java 语法类似，可以无成本切换。&lt;/p&gt;
&lt;h1 id=&#34;设计理念&#34;&gt;设计理念&lt;/h1&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4a5323e837144767bf9b77159c816859.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;ngrinder-架构&#34;&gt;nGrinder 架构&lt;/h1&gt;
&lt;p&gt;nGrinder 系统架构如下：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/a492956cd0794cd68032605a2318fbb3.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;nGrinder 的底层是 Grinder，基于 Grinder 的 console 和 agent，nGrinder 封装成了 controller 和 agent：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;controller 提供了 Web 页面创建测试脚本和配置运行策略，这是 nGrinder 比较突出的特色了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;agent 虚拟用户生成器，创建压测负载。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当用户创建 test 后，会先创建一个 console，AgentControllerServer 会根据 console 分配相应的 agent，AgentControllerServer 管理着 agent pool，负责 agent 的创建和释放。然后 console 将 test 脚本和资源多个已分配的 agent，并控制整个 test flow 直到测试结束。测试结束后，agent 会回收至 AgentControllerServer 以便其他 test 使用，console 也会回收至 ConsoleManager。&lt;/p&gt;</description>
    </item>
    <item>
      <title>JMeter 扩展 Java 请求</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E6%89%A9%E5%B1%95java%E8%AF%B7%E6%B1%82/</link>
      <pubDate>Tue, 13 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E6%89%A9%E5%B1%95java%E8%AF%B7%E6%B1%82/</guid>
      <description>本文将通过 WebRTC 本地音视频流压测脚本，来说明 JMeter 如何扩展 Java 请求。</description>
    </item>
    <item>
      <title>JMeter 响应断言够用了</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%93%8D%E5%BA%94%E6%96%AD%E8%A8%80%E5%A4%9F%E7%94%A8%E4%BA%86/</link>
      <pubDate>Mon, 12 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%93%8D%E5%BA%94%E6%96%AD%E8%A8%80%E5%A4%9F%E7%94%A8%E4%BA%86/</guid>
      <description>JMeter 断言用来对服务器的响应数据进行验证，其中的响应断言基本能完成大多数的验证工作，但是 JMeter 还是提供了适用于多个不同场景的断言元件，一共 16 个，如下图所示：</description>
    </item>
    <item>
      <title>JMeter 定时器设置延迟与同步</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%AE%9A%E6%97%B6%E5%99%A8%E8%AE%BE%E7%BD%AE%E5%BB%B6%E8%BF%9F%E4%B8%8E%E5%90%8C%E6%AD%A5/</link>
      <pubDate>Sun, 11 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%AE%9A%E6%97%B6%E5%99%A8%E8%AE%BE%E7%BD%AE%E5%BB%B6%E8%BF%9F%E4%B8%8E%E5%90%8C%E6%AD%A5/</guid>
      <description>JMeter 定时器一般用来设置延迟与同步。它的作用域和优先级如下：</description>
    </item>
    <item>
      <title>JMeter 逻辑控制器完整介绍</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E9%80%BB%E8%BE%91%E6%8E%A7%E5%88%B6%E5%99%A8%E5%AE%8C%E6%95%B4%E4%BB%8B%E7%BB%8D/</link>
      <pubDate>Sat, 10 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E9%80%BB%E8%BE%91%E6%8E%A7%E5%88%B6%E5%99%A8%E5%AE%8C%E6%95%B4%E4%BB%8B%E7%BB%8D/</guid>
      <description>JMeter 逻辑控制器可以对元件的执行逻辑进行控制，就像编程一样，实现业务需求。</description>
    </item>
    <item>
      <title>JMeter36 个内置函数及 11 个新增函数介绍</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter36%E4%B8%AA%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0%E5%8F%8A11%E4%B8%AA%E6%96%B0%E5%A2%9E%E5%87%BD%E6%95%B0%E4%BB%8B%E7%BB%8D/</link>
      <pubDate>Fri, 09 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter36%E4%B8%AA%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0%E5%8F%8A11%E4%B8%AA%E6%96%B0%E5%A2%9E%E5%87%BD%E6%95%B0%E4%BB%8B%E7%BB%8D/</guid>
      <description>JMeter 内置了 36 个函数，这些函数可以通过函数助手进行编辑和测试。了解这些函数，不仅能提高 JMeter 的使用熟练度，也有助于知晓测试工具或测试框架通用的函数有哪些，在自主设计时，作为参考借鉴。</description>
    </item>
    <item>
      <title>Java 单元测试 Mock 框架 Mockito 入门介绍</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/java%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95mock%E6%A1%86%E6%9E%B6mockito%E5%85%A5%E9%97%A8%E4%BB%8B%E7%BB%8D/</link>
      <pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/java%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95mock%E6%A1%86%E6%9E%B6mockito%E5%85%A5%E9%97%A8%E4%BB%8B%E7%BB%8D/</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;Mockito 是 Java 的单元测试 Mock 框架。它的 logo 是一杯古巴最著名的鸡尾酒 Mojito，Ｍojito 鸡尾酒，源自古巴的哈瓦那，带有浓厚的加勒比海风情。并不浓烈，但是喝一杯下去，脸上会泛起红晕，象少女的羞涩。味道很清新，有一点青涩、有点甜蜜。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/72142d20108948cfa0db66a588a95d8d.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;stub--mock&#34;&gt;Stub &amp;amp; Mock&lt;/h1&gt;
&lt;p&gt;Stub 和 Mock 是 Test Double 类型中的 2 种。Test Double 一共有 5 种类型，Dummy，Stub，Spy，Mock，Fake。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8b7d2c986e6845fb81ab9383fba03b16.gif&#34;&gt;&lt;/p&gt;
&lt;p&gt;Test Double 是测试复制品，用来统称模拟真实对象的假对象。因使用场景有略微不同，而有这 5 种类型。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dummy，通常只用来填充参数列表。有可能是 null 对象引用，或 Object 类实例等。&lt;/li&gt;
&lt;li&gt;Fake，是简化版的实现，比如基于内存实现的数据库，不会真的去做数据库操作，用简单的 HashMap 来存放数据。&lt;/li&gt;
&lt;li&gt;Stub，Stub 用来替代 SUT(System Under Test)依赖的组件，但是只模拟一个外部依赖，不做断言。&lt;/li&gt;
&lt;li&gt;Spy，介于 Stub 和 Mock 之间。如果真实对象没有被打桩，当调用 Spy 对象时，真实对象也会被调用。&lt;/li&gt;
&lt;li&gt;Mock，可以理解为 Stub+Verification，既模拟外部依赖，也会定义预期结果。&lt;/li&gt;
&lt;/ul&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;MailService&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;send&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Message&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;msg&lt;span style=&#34;color:#1f2328&#34;&gt;);&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 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;p&gt;先写个 Stub，&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;MailServiceStub&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;implements&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;MailService&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;private&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;List&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt;Message&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;messages&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;new&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;ArrayList&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt;Message&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;gt;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;send&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Message&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;msg&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;messages&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;add&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;msg&lt;span style=&#34;color:#1f2328&#34;&gt;);&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;numberSent&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;messages&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;size&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &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;&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 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;p&gt;实现 Stub 的状态验证，&lt;/p&gt;</description>
    </item>
    <item>
      <title>JMeter 四种参数化方式</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%9B%9B%E7%A7%8D%E5%8F%82%E6%95%B0%E5%8C%96%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%9B%9B%E7%A7%8D%E5%8F%82%E6%95%B0%E5%8C%96%E6%96%B9%E5%BC%8F/</guid>
      <description>JMeter 参数化是指&lt;strong&gt;把固定的数据动态化&lt;/strong&gt;，这样更贴合实际的模拟用户请求，比如模拟多个不同账号。JMeter 一共有四种参数化方式，分别是：</description>
    </item>
    <item>
      <title>Allure 测试报告完整学习笔记</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/allure%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E5%AE%8C%E6%95%B4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/allure%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A%E5%AE%8C%E6%95%B4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;目录&#34;&gt;目录&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;简介&lt;/li&gt;
&lt;li&gt;安装 Allure&lt;/li&gt;
&lt;li&gt;Allure 测试报告的结构&lt;/li&gt;
&lt;li&gt;Java TestNG 集成 Allure Report&lt;/li&gt;
&lt;li&gt;Python Pytest 集成 Allure Report&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;简介&#34;&gt;简介&lt;/h1&gt;
&lt;p&gt;假如你想让测试报告变得漂亮一点，那你一定会在搜索引擎中找到 Allure 测试报告的。Allure 官方给出了个在线网站，访问以下链接就能一睹为快：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://demo.qameta.io/allure/&#34;&gt;https://demo.qameta.io/allure/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/12513ca6699c49569b795699f0d97579.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;可以在左下角切换为中文报告：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/0e022a1416a444058f2241cbf2a00d8c.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;如此好看的测试报告，哪个老板不喜欢呢？&lt;/p&gt;
&lt;h1 id=&#34;安装-allure&#34;&gt;安装 Allure&lt;/h1&gt;
&lt;p&gt;1.在 GitHub releases：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/allure-framework/allure2/releases&#34;&gt;https://github.com/allure-framework/allure2/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/97e74735b6524fb687af5c5ec894811c.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;或者 Maven Central：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/&#34;&gt;https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/6d5dc013fc9a48bf91c95fbd41be42cb.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;下载安装包并解压。&lt;/p&gt;
&lt;p&gt;2.找到 bin 目录，Windows 使用&lt;code&gt;allure.bat&lt;/code&gt;，Unix 使用&lt;code&gt;allure&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d3ba1cb635c846ecbd32e9377284c68a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;3.为了后续方便使用，可以把 bin 完整路径添加到&lt;strong&gt;系统环境变量 PATH&lt;/strong&gt;中，然后在命令行直接输入&lt;strong&gt;allure&lt;/strong&gt;命令。&lt;/p&gt;
&lt;p&gt;官方也给出了通过 Linux 的 ppa、Mac 的 brew、Windows 的 scoop 的安装方式，不过对于咱们老百姓来说，不如手动下载包来安装实在。&lt;/p&gt;
&lt;p&gt;安装后就可以检查是否安装成功了：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ allure --version
2.17.2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;因为 Allure 是用 Java 写的，所以如果无法运行，那么要检查下有没有安装 Java。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;allure-测试报告的结构&#34;&gt;Allure 测试报告的结构&lt;/h1&gt;
&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ce7256011e964d36a061220166d12c1d.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>JMeter 关联陌生又熟悉</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%85%B3%E8%81%94%E9%99%8C%E7%94%9F%E5%8F%88%E7%86%9F%E6%82%89/</link>
      <pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%85%B3%E8%81%94%E9%99%8C%E7%94%9F%E5%8F%88%E7%86%9F%E6%82%89/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;jmeter-关联是什么&#34;&gt;JMeter 关联是什么&lt;/h1&gt;
&lt;p&gt;JMeter 关联，这几个字看着可能会有点陌生，实际上却是工作中经常会做的一件事情，尤其是接口自动化，它指的是把一个接口的响应作为另一个接口的参数，从而&lt;strong&gt;把接口关联起来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;JMeter 关联是通过后置处理器来实现的，本文将介绍两个主流提取器：正则表达式提取器和 JSON JMESPath 提取器。&lt;/p&gt;
&lt;h1 id=&#34;正则表达式提取器&#34;&gt;正则表达式提取器&lt;/h1&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ce535876d05748b98a16512d1e2f536e.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;name&#34;&gt;Name&lt;/h2&gt;
&lt;p&gt;最好有业务含义的名称。&lt;/p&gt;
&lt;h2 id=&#34;comments&#34;&gt;Comments&lt;/h2&gt;
&lt;p&gt;可以为空的注释。&lt;/p&gt;
&lt;h2 id=&#34;apply-to&#34;&gt;Apply to&lt;/h2&gt;
&lt;p&gt;匹配范围。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Main sample and sub-samples&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;匹配当前父取样器及其子取样器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Main sample only&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;仅匹配当前父取样器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sub-samples only&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;仅匹配子取样器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JMeter Variable Name to use&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;匹配 JMeter 变量。&lt;/p&gt;
&lt;h2 id=&#34;field-to-check&#34;&gt;Field to check&lt;/h2&gt;
&lt;p&gt;匹配响应的字段，共七个选项。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Body&lt;/strong&gt;：主体，最常用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Body(unescaped)&lt;/strong&gt; ：替换了所有的 html 转义符的响应主体内容，注意 html 转义符处理时不考虑上下文，因此可能有不正确的转换，不太建议使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Body as a Document&lt;/strong&gt;：从不同类型的文件中提取文本，注意这个选项比较影响性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Response Headers&lt;/strong&gt;：响应 Headers。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Request Headers&lt;/strong&gt;：请求 Headers。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;URL&lt;/strong&gt;：请求 URL。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Response Code&lt;/strong&gt;：响应代码，比如 200、404 返回码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Response Message&lt;/strong&gt;：响应信息，比如“成功”、“OK”字样。&lt;/p&gt;
&lt;h2 id=&#34;name-of-created-variable&#34;&gt;Name of created variable&lt;/h2&gt;
&lt;p&gt;创建的变量名，比如变量名为 token，通过&lt;code&gt;${token}&lt;/code&gt;进行访问。&lt;/p&gt;</description>
    </item>
    <item>
      <title>JUnit5 依赖注入与测试接口</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E4%B8%8E%E6%B5%8B%E8%AF%95%E6%8E%A5%E5%8F%A3/</link>
      <pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E4%B8%8E%E6%B5%8B%E8%AF%95%E6%8E%A5%E5%8F%A3/</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;以前的 JUnit 的类构造方法和测试方法都是不能有参数的，&lt;strong&gt;JUnit Jupiter 有一个颠覆性的改进，就是允许它们有入参，这样就能做依赖注入了。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你对 pytest 的 fixture 有了解的话，就知道这个技术是多么的强大。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ParameterResolver 是一个接口类，类构造方法和测试方法在运行时，必须由被注册的 ParameterResolver 进行解析。JUnit Jupiter 有三个自动注册的内置解析器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TestInfoParameterResolver 参数类型为 TestInfo&lt;/li&gt;
&lt;li&gt;RepetitionInfoParameterResolver 参数类型为 RepetitionInfo&lt;/li&gt;
&lt;li&gt;TestReporterParameterResolver 参数类型为 TestReporter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TestInfo&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TestInfo 包含 the display name, the test class, the test method, and associated tags 等信息。&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;import static&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.Assertions.assertEquals&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;&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 static&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.Assertions.assertTrue&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.BeforeEach&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;&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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.DisplayName&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;&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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.Tag&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;&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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.Test&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;&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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.TestInfo&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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;@DisplayName&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;TestInfo Demo&amp;#34;&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;&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;TestInfoDemo&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;TestInfoDemo&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;TestInfo&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;assertEquals&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;TestInfo Demo&amp;#34;&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;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;getDisplayName&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@BeforeEach&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;TestInfo&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;String&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;displayName&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;getDisplayName&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;assertTrue&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;displayName&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;equals&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;TEST 1&amp;#34;&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;&lt;span style=&#34;color:#0550ae&#34;&gt;||&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;displayName&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;equals&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;test2()&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@DisplayName&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;TEST 1&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Tag&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;my-tag&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;test1&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;TestInfo&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;assertEquals&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;TEST 1&amp;#34;&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;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;getDisplayName&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;assertTrue&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;testInfo&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;getTags&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;().&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;contains&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;my-tag&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;test2&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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 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;p&gt;&lt;strong&gt;RepetitionInfo&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>pytest 用例查找原理</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E7%94%A8%E4%BE%8B%E6%9F%A5%E6%89%BE%E5%8E%9F%E7%90%86/</link>
      <pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E7%94%A8%E4%BE%8B%E6%9F%A5%E6%89%BE%E5%8E%9F%E7%90%86/</guid>
      <description>当执行 pytest 命令时，pytest 会从 project 中查找 test 来执行。本文先从几个路径相关的概念讲起，这样便于理解 pytest 在遍历路径时的走向。</description>
    </item>
    <item>
      <title>JMeter100 个线程竟然只模拟出 1 个并发</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter100%E4%B8%AA%E7%BA%BF%E7%A8%8B%E7%AB%9F%E7%84%B6%E5%8F%AA%E6%A8%A1%E6%8B%9F%E5%87%BA1%E4%B8%AA%E5%B9%B6%E5%8F%91/</link>
      <pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter100%E4%B8%AA%E7%BA%BF%E7%A8%8B%E7%AB%9F%E7%84%B6%E5%8F%AA%E6%A8%A1%E6%8B%9F%E5%87%BA1%E4%B8%AA%E5%B9%B6%E5%8F%91/</guid>
      <description>线程组编辑区如下：</description>
    </item>
    <item>
      <title>JUnit5 参数化测试的几种方式</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E5%8F%82%E6%95%B0%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E5%8F%82%E6%95%B0%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/</guid>
      <description>参数化测试一直是津津乐道的话题，我们都知道 JMeter 有四种参数化方式：用户自定义变量、用户参数、CSV 文件、函数助手，那么 JUnit5 有哪些参数化测试的方式呢？</description>
    </item>
    <item>
      <title>pytest 断言技巧</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E6%96%AD%E8%A8%80%E6%8A%80%E5%B7%A7/</link>
      <pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E6%96%AD%E8%A8%80%E6%8A%80%E5%B7%A7/</guid>
      <description>pytest 的断言把 Python 语言简洁的优点发挥的淋漓尽致，因为它用的就是 Python 的标准断言&lt;strong&gt;assert&lt;/strong&gt;。</description>
    </item>
    <item>
      <title>老板给我安排了个测试环境治理的活</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E8%80%81%E6%9D%BF%E7%BB%99%E6%88%91%E5%AE%89%E6%8E%92%E4%BA%86%E4%B8%AA%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83%E6%B2%BB%E7%90%86%E7%9A%84%E6%B4%BB/</link>
      <pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E8%80%81%E6%9D%BF%E7%BB%99%E6%88%91%E5%AE%89%E6%8E%92%E4%BA%86%E4%B8%AA%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83%E6%B2%BB%E7%90%86%E7%9A%84%E6%B4%BB/</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;测试环境治理专项是在我入职 2 个月接手的活，用“烂摊子”来形容一点也不为过。年久失修，需求基本没有在测试环境使用，想用也有很多问题。人员缺乏信心，之前负责测试环境的同学抱怨极多，外部推不动，内部又没有技术能力支撑，负能量已经蔓延到整个团队。当然，我也是其中一员，这事确实难搞。在技术部&lt;strong&gt;测试环境平台化&lt;/strong&gt;的大方向下，需要每个组出一个接口人来牵头，考虑到我以前写过测试平台，乱七八糟的事都整过，老板就指定了我。&lt;/p&gt;
&lt;h1 id=&#34;what测试环境是什么&#34;&gt;What，测试环境是什么？&lt;/h1&gt;
&lt;p&gt;测试环境是一套能够支撑业务系统运行的环境，是相对于预发环境和生产环境来说完全隔离的环境。&lt;strong&gt;测试环境=应用+数据+配置+依赖服务&lt;/strong&gt;。应用包含系统应用和中间件等，数据就是测试物料，配置针对测试环境应该有单独的一套，依赖服务要跟外部联调好以后整个环境才是可用的。测试环境治理就是通过内部搭建和外部联调，保障业务系统能够在完全隔离的环境中运行。&lt;/p&gt;
&lt;h1 id=&#34;why为什么要做测试环境&#34;&gt;Why，为什么要做测试环境？&lt;/h1&gt;
&lt;p&gt;测试环境是为了隔离。如果只有一套生产环境，那么开发、测试、上线等活动，可能会对线上系统稳定性造成影响，可能会污染线上用户数据，可能引起客户投诉甚至资产损失，所以&lt;strong&gt;需要有一套完全隔离的测试环境&lt;/strong&gt;。&lt;/p&gt;
&lt;h1 id=&#34;who谁来负责测试环境&#34;&gt;Who，谁来负责测试环境？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;每个测试人员&lt;/strong&gt;都需要具备测试环境搭建能力。当然可以推动运维或研发来搭建测试环境，但是测试人员需要具备搭建能力，能够自主编译，构建，部署，遇到问题能够根据日志做初步定位，将问题抛出推动研发或外部依赖方解决。&lt;/p&gt;
&lt;h1 id=&#34;when什么时间做测试环境&#34;&gt;When，什么时间做测试环境？&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/366febbd0a19441881cdffe1a55b1ea4.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;where从哪里入手测试环境&#34;&gt;Where，从哪里入手测试环境？&lt;/h1&gt;
&lt;p&gt;测试环境治理需要&lt;strong&gt;从上往下&lt;/strong&gt;才能推动，先有文化，才有行动。假如只有某个部门想做测试环境治理，外部依赖基本上是推不动的，一个人呐喊得再愤怒其他人也可能无动于衷。所以得先把测试环境治理这件事，跟老板沟通，从高层入手，把它定义为一项政治任务，从上往下层层递进。&lt;/p&gt;
&lt;h1 id=&#34;how怎么做测试环境&#34;&gt;How，怎么做测试环境？&lt;/h1&gt;
&lt;p&gt;工欲善其事，必先利其器，公司的基建比较完善，给测试人员搭建测试环境降低了很大难度。设想一下，假如只能折腾 Docker 和 K8S，那可能需要专门的运维才能搞得定环境，对测试来说门槛就会比较高了，除了少数技术能力强的，其他同学基本没有什么可操作性。&lt;strong&gt;测试环境平台化就是武器&lt;/strong&gt;。公司有个部署平台叫做 JDOS，日常运维都是在这上面操作，没有运维人员，环境全靠开发人员和测试人员自己整。JDOS 已经是页面化操作了，但它对测试环境没有特殊的应用场景，是个大而全的平台，所以二级部门开了一个测试环境平台，Env，&amp;ldquo;一款全流程自动化测试环境搭建的利器&amp;rdquo;。&lt;/p&gt;
&lt;p&gt;Env 的核心思想是&lt;code&gt;1+N&lt;/code&gt;。&lt;code&gt;1&lt;/code&gt;代表稳定分组（分组≈Pod），搭建稳定分组需要经过创建系统，创建应用，创建集群，申请机器，代码编译构建，部署镜像，修改配置等操作，需要选取 master 稳定版本，这样才能对外提供稳定服务。&lt;code&gt;N&lt;/code&gt;是克隆&lt;code&gt;1&lt;/code&gt;产生的多个分组，相互隔离，能让开发同学复用测试搭建好的环境，完成冒烟自测等过程；也能在多业务联调测试时，通过平台快速搭建一套全链路测试环境。&lt;/p&gt;
&lt;p&gt;Env 提供了环境监控的功能。对于搭建好的稳定环境，它可以每天监控机器是否可用，服务是否正常，镜像是否老旧，等等等，让你及时维护环境，保障稳定性。&lt;/p&gt;
&lt;p&gt;Env 提供了一键搭建的功能。新克隆的环境，只要点一下“一键搭建”，就会基于稳定分组，自动化搭建一套新环境，大大节约了时间成本。&lt;/p&gt;
&lt;p&gt;Env 提供了拓扑画图的功能。将多个应用拖到一张拓扑图里面，根据依赖关系连线，能够直观了解到应用之间的依赖关系。基于拓扑来克隆新环境，也能批量克隆多个应用。假如多项目需求并行迭代，想要多套联调的测试环境，就可以克隆多套新环境。&lt;/p&gt;
&lt;p&gt;Env 还解决了一个资源浪费的问题，以前搭建测试环境比较随意，申请资源也很容易，其实会导致很多机器用过后，一直空置在那里。Env 收拢了申请资源入口，基于 Env 来创建稳定分组，只能有 1 个，想要更多必须走审批。而对于克隆的新环境，也会有期限，14 天以后会自动释放，想要延期也必须走审批。&lt;/p&gt;
&lt;p&gt;我所负责的第一件事，就是用 Env 来搭建一张拓扑图，把组内所有应用都从 JDOS 迁移到 Env 上，&lt;strong&gt;维持稳定环境对外提供支撑对接，克隆环境对内支持多项目并行&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;搭建测试环境只是起步，&lt;strong&gt;测试环境治理的目标是需求能够使用测试环境&lt;/strong&gt;。对于外部依赖少的业务模块来说，这相对容易。对于外部依赖很多的业务模块来说，这就很难了。那么该如何推动这件事呢？&lt;/p&gt;
&lt;p&gt;第 1 个策略是共建，让研发和测试一起来搭建和维护测试环境。&lt;/p&gt;
&lt;p&gt;第 2 个策略是做功能验证，梳理出链路用例，跟踪验证结果，识别卡点问题：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/993f842d5d314b23a877f507494f3697.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;对于卡点问题的处理，把干系人单独拉群对，响应超过 3 天就往上抛，&lt;strong&gt;一定会遇到推不动的问题，那么办法只能找自己的上游负责人，层层向上反馈，让上面的负责人去找对方负责人，然后推动对方的下游同学来协助解决&lt;/strong&gt;。卡点问题记录后需要做问题分类，做好统计，这既是工作量的体现，也是每周进度汇报的依据：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/546328b1b59744b9951e185dea9319fd.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/687bb904572e48f9ad64703e7e3a6f86.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;第 3 个策略就是制定 kpi，强制大家使用测试环境完成绩效。&lt;/p&gt;
&lt;h1 id=&#34;how-much测试环境做到什么程度&#34;&gt;How much，测试环境做到什么程度？&lt;/h1&gt;
&lt;p&gt;测试环境的 kpi 能反应出测试环境应该做到什么程度，可以使用&lt;strong&gt;需求使用测试环境占比&lt;/strong&gt;和&lt;strong&gt;测试环境 bug 占比&lt;/strong&gt;。需求使用测试环境占比的计算公式是&lt;code&gt;打标了测试环境的提测卡片数/总的提测卡片数&lt;/code&gt;，测试在点击开始测试时，选择是否使用测试环境进行打标。测试环境 bug 占比的计算公式是&lt;code&gt;测试环境bug/(测试环境bug+预发环境bug+生产环境bug)&lt;/code&gt;，在提交缺陷时选择环境类型进行打标。测试环境 bug 占比的意义更大，因为使用测试环境与否，最终的体现还是落在缺陷上面的，比较容易量化，需求使用测试环境占比不是很确定，需求多少功能在测试环境使用了，相对来说不是很好度量，但也可以作为辅助指标进行观察。&lt;/p&gt;</description>
    </item>
    <item>
      <title>HttpRunner3 的$符号是如何解析的</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84%E7%AC%A6%E5%8F%B7%E6%98%AF%E5%A6%82%E4%BD%95%E8%A7%A3%E6%9E%90%E7%9A%84/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84%E7%AC%A6%E5%8F%B7%E6%98%AF%E5%A6%82%E4%BD%95%E8%A7%A3%E6%9E%90%E7%9A%84/</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;code&gt;$&lt;/code&gt;符号在很多测试框架中都会拿来用做变量解析，大名鼎鼎的 JMeter 就有这个用法。HttpRunner3 也支持&lt;code&gt;$&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;Step&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;    RunRequest&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:#0550ae&#34;&gt;.&lt;/span&gt;post&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;/login&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:#0550ae&#34;&gt;.&lt;/span&gt;with_headers&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;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Content-Type&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;application/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 style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;with_json&lt;span style=&#34;color:#1f2328&#34;&gt;({&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;username&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;dongfanger&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;password&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;123456&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:#0550ae&#34;&gt;.&lt;/span&gt;extract&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:#0550ae&#34;&gt;.&lt;/span&gt;with_jmespath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;body.token&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;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;        &lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;validate&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:#0550ae&#34;&gt;.&lt;/span&gt;assert_equal&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;status_code&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;200&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;Step&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;    RunRequest&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:#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;searchSku?skuName=电子书&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:#0550ae&#34;&gt;.&lt;/span&gt;with_headers&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;&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 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;        &lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;extract&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:#0550ae&#34;&gt;.&lt;/span&gt;with_jmespath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;body.skuId&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;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 style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;with_jmespath&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;body.price&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;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;        &lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;validate&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:#0550ae&#34;&gt;.&lt;/span&gt;assert_equal&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;status_code&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;200&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;p&gt;从&lt;code&gt;登录&lt;/code&gt;提取的变量&lt;code&gt;token&lt;/code&gt;，在&lt;code&gt;搜索商品&lt;/code&gt;中使用&lt;code&gt;$token&lt;/code&gt;来引用。&lt;/p&gt;
&lt;h1 id=&#34;parserpy&#34;&gt;parser.py&lt;/h1&gt;
&lt;p&gt;HttpRunner 对&lt;code&gt;$&lt;/code&gt;符号的解析是在 parser.py 文件中实现的：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/3881ed1c0d374c3da2d839e5d738265a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这些方法可以分为四类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;url&lt;/p&gt;
&lt;p&gt;&lt;code&gt;build_url()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;变量&lt;/p&gt;
&lt;p&gt;&lt;code&gt;extract_variables()&lt;/code&gt;  &lt;code&gt;get_mapping_variable()&lt;/code&gt;  &lt;code&gt;parse_variables_mapping()&lt;/code&gt;  &lt;code&gt;regex_findall_variables()&lt;/code&gt;  &lt;code&gt;parse_data()&lt;/code&gt;  &lt;code&gt;parse_string()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;函数&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_mapping_function()&lt;/code&gt;  &lt;code&gt;parse_function_params()&lt;/code&gt;  &lt;code&gt;regex_findall_functions()&lt;/code&gt;  &lt;code&gt;parse_string_value()&lt;/code&gt;  &lt;code&gt;parse_data()&lt;/code&gt;  &lt;code&gt;parse_string()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;参数化（&lt;code&gt;&amp;quot;${parameterize(account.csv)}&amp;quot;&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;parse_parameters()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&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;HttpRunner3 在运行时的所有变量都是存储在&lt;code&gt;__session_variables&lt;/code&gt;这个字典中的：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d8ba076be0004516a4f07e77a353c1b1.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;在脚本开始时会把 config 的预设变量加载进来，然后会在执行 Step 测试步骤时，把各个步骤的变量也放到全局变量池里面。&lt;/p&gt;
&lt;h1 id=&#34;解析过程&#34;&gt;解析过程&lt;/h1&gt;
&lt;p&gt;在 runner.py 的第 350 行代码能看到解析变量的调用：&lt;/p&gt;</description>
    </item>
    <item>
      <title>JaCoCo 增量覆盖率的基本实现原理</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jacoco%E5%A2%9E%E9%87%8F%E8%A6%86%E7%9B%96%E7%8E%87%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jacoco%E5%A2%9E%E9%87%8F%E8%A6%86%E7%9B%96%E7%8E%87%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;什么是增量覆盖率&#34;&gt;什么是增量覆盖率&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/fd75a2d267c34c9f9e5e1344e30f39de.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;如图所示，在 master 分支提交了 HelloController，然后从 master 拉了个新分支 test；提交了第 1 次代码，增加了 WorldController；提交了第 2 次代码，增加了 DonController。增量的获取方式有两种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;版本对比：在分支上，第 2 次提交和第 1 次提交版本对比的增量代码，就是 DonController；第 2 次提交和刚从 master 拉取分支时版本对比的增量代码，除了 DonController 还有 WorldController；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分支对比：把 test 分支和 master 分支进行对比，增量代码就是 DonController 和 WorldController；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;增量，就是通过版本对比或分支对比，多出来的新增加的那部分代码。覆盖率是指程序运行以后，有多少代码被执行到了，除以总的代码数算出来的，即&lt;code&gt;覆盖率=执行代码行数/总代码行数&lt;/code&gt;。增量覆盖率是针对增量代码来计算的，也就是&lt;code&gt;增量覆盖率=执行代码行数/增量代码行数&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;基本实现原理&#34;&gt;基本实现原理&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d4e75938a47841e2a138fe08e79f3e4e.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;使用 JaCoCo 的 On-the-fly 模式，以 tcpserver 方式启动，将远程机器的覆盖率数据通过 TCP 通信 dump 到本地 jacoco.exec 文件，二次开发 JaCoCo 源码，实现增量覆盖率分析和输出 HTML 报告。&lt;/p&gt;
&lt;h2 id=&#34;jacoco-默认覆盖率&#34;&gt;JaCoCo 默认覆盖率&lt;/h2&gt;
&lt;p&gt;JaCoCo 是一个开源的覆盖率工具，它的 On-the-fly 模式，无须侵入应用启动脚本，只需在 JVM 中通过-javaagent 参数指定 jar 文件启动的代理程序，代理程序在 ClassLoader 装载一个 class 前判断是否需要注入 class 文件，将统计代码插入 class，覆盖率分析就可以在 JVM 执行的过程中完成。&lt;/p&gt;</description>
    </item>
    <item>
      <title>JMeter 测试计划配置项解析</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E6%B5%8B%E8%AF%95%E8%AE%A1%E5%88%92%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%A7%A3%E6%9E%90/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E6%B5%8B%E8%AF%95%E8%AE%A1%E5%88%92%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%A7%A3%E6%9E%90/</guid>
      <description>测试计划（Test Plan），是 JMeter 目录树的初始根节点，它的编辑区如下：</description>
    </item>
    <item>
      <title>JUnit5 的条件测试、嵌套测试、重复测试</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E7%9A%84%E6%9D%A1%E4%BB%B6%E6%B5%8B%E8%AF%95%E5%B5%8C%E5%A5%97%E6%B5%8B%E8%AF%95%E9%87%8D%E5%A4%8D%E6%B5%8B%E8%AF%95/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E7%9A%84%E6%9D%A1%E4%BB%B6%E6%B5%8B%E8%AF%95%E5%B5%8C%E5%A5%97%E6%B5%8B%E8%AF%95%E9%87%8D%E5%A4%8D%E6%B5%8B%E8%AF%95/</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;JUnit5 支持条件注解，根据布尔值判断是否执行测试。&lt;/p&gt;
&lt;h2 id=&#34;自定义条件&#34;&gt;自定义条件&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;@EnabledIf&lt;/code&gt;和&lt;code&gt;@DisabledIf&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@EnabledIf&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;customCondition&amp;#34;&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;&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;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;enabled&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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:#fff&#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;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@DisabledIf&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;customCondition&amp;#34;&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;&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;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;disabled&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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:#fff&#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;boolean&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;customCondition&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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 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;p&gt;其中 customCondition()方法用来返回布尔值，它可以接受一个 ExtensionContext 类型的参数。如果定义在测试类外部，那么需要是 static 方法。&lt;/p&gt;
&lt;h2 id=&#34;内置条件&#34;&gt;内置条件&lt;/h2&gt;
&lt;p&gt;JUnit5 的&lt;code&gt;org.junit.jupiter.api.condition&lt;/code&gt;包中内置了一些条件注解。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;操作系统条件&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@EnabledOnOs&lt;/code&gt;和&lt;code&gt;DisabledOnOs&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@EnabledOnOs&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;MAC&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;onlyOnMacOs&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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:#fff&#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;@TestOnMac&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;testOnMac&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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:#fff&#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;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@EnabledOnOs&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;LINUX&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;MAC&lt;span style=&#34;color:#fff&#34;&gt; &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;&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;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;onLinuxOrMac&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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:#fff&#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;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@DisabledOnOs&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;WINDOWS&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;notOnWindows&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#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 style=&#34;color:#fff&#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:#fff&#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;@Target&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;ElementType&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;METHOD&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;&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;@Retention&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;RetentionPolicy&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;RUNTIME&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;&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;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@EnabledOnOs&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;MAC&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#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;@interface&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;TestOnMac&lt;span style=&#34;color:#fff&#34;&gt; &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;&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 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;p&gt;&lt;strong&gt;JRE 条件&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>pytest 参数化进阶</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E5%8F%82%E6%95%B0%E5%8C%96%E8%BF%9B%E9%98%B6/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E5%8F%82%E6%95%B0%E5%8C%96%E8%BF%9B%E9%98%B6/</guid>
      <description>用过 unittest 的朋友，肯定知道可以借助 DDT 实现参数化。用过 JMeter 的朋友，肯定知道 JMeter 自带了 4 种参数化方式（见参考资料）。pytest 同样支持参数化，而且很简单很实用。</description>
    </item>
    <item>
      <title>端到端自动化的开源框架</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E7%AB%AF%E5%88%B0%E7%AB%AF%E8%87%AA%E5%8A%A8%E5%8C%96%E7%9A%84%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E7%AB%AF%E5%88%B0%E7%AB%AF%E8%87%AA%E5%8A%A8%E5%8C%96%E7%9A%84%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;什么是-playwright&#34;&gt;什么是 Playwright？&lt;/h1&gt;
&lt;p&gt;Playwright 是一个开源框架，用于对 Web 应用进行端到端测试。&lt;/p&gt;
&lt;h1 id=&#34;什么是端到端测试&#34;&gt;什么是端到端测试？&lt;/h1&gt;
&lt;p&gt;这里的“端”，不是指 A 模块或 B 模块，而是可以理解为从前端到后端。举个例子，对于登录功能来说，如果只测前端，那么测试范围就是前端页面，可能会对后端接口进行 mock。如果只测后端，那么测试范围就是后端接口等。而端到端呢，就是前端输入用户名密码，点击登录，与后端交互，查询数据库，鉴权，后端返回登录成功，前端进行页面跳转。端到端测试，可以理解为全流程测试，也可以理解为模拟用户场景测试。&lt;/p&gt;
&lt;h1 id=&#34;playwright-框架特点&#34;&gt;Playwright 框架特点&lt;/h1&gt;
&lt;p&gt;官方网站首页：&lt;a href=&#34;https://playwright.dev/python/&#34;&gt;https://playwright.dev/python/&lt;/a&gt;&lt;/p&gt;
&lt;p&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/24bbbd01c6594bc08a753059ab61356f.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;本文及后续文章均选择 Python。&lt;/p&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;p&gt;安装 Python 的 Playwright，这里结合了 pytest：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pip install pytest-playwright
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;成功后安装浏览器：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;playwright install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里也可以不安装浏览器，而是在脚本里面指定现有浏览器路径。&lt;/p&gt;
&lt;h2 id=&#34;示例&#34;&gt;示例&lt;/h2&gt;
&lt;p&gt;test_example.py&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import re
from playwright.sync_api import Page, expect

def test_has_title(page: Page):
    page.goto(&amp;#34;https://playwright.dev/&amp;#34;)

    # Expect a title &amp;#34;to contain&amp;#34; a substring.
    expect(page).to_have_title(re.compile(&amp;#34;Playwright&amp;#34;))

def test_get_started_link(page: Page):
    page.goto(&amp;#34;https://playwright.dev/&amp;#34;)

    # Click the get started link.
    page.get_by_role(&amp;#34;link&amp;#34;, name=&amp;#34;Get started&amp;#34;).click()

    # Expects page to have a heading with the name of Installation.
    expect(page.get_by_role(&amp;#34;heading&amp;#34;, name=&amp;#34;Installation&amp;#34;)).to_be_visible()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;执行 pytest 后默认会选择 chrome 在 headless 模式下运行，可以通过日志观察运行过程。&lt;/p&gt;</description>
    </item>
    <item>
      <title>HttpRunner3 的变量是如何传递的</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84%E5%8F%98%E9%87%8F%E6%98%AF%E5%A6%82%E4%BD%95%E4%BC%A0%E9%80%92%E7%9A%84/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84%E5%8F%98%E9%87%8F%E6%98%AF%E5%A6%82%E4%BD%95%E4%BC%A0%E9%80%92%E7%9A%84/</guid>
      <description>HttpRunner3 的变量可以在测试类的用例配置中通过&lt;code&gt;variables&lt;/code&gt;添加，也可以在测试步骤中使用&lt;code&gt;extract()&lt;/code&gt;、&lt;code&gt;with_jmespath()&lt;/code&gt;提取出来放到变量&lt;code&gt;x&lt;/code&gt;，再用&lt;code&gt;$x&lt;/code&gt;传递给下一个接口使用，比如登录到下单流程的部分测试脚本如下：</description>
    </item>
    <item>
      <title>JMeter 元件作用域实践指南</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%85%83%E4%BB%B6%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%85%83%E4%BB%B6%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</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/08bb056c95a34670a750060f1e04625a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;为了能调用进入房间接口，需要从考场接口获取考场 token。为了调用考场接口，需要从登陆接口获取登陆 token。元件说明如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;学生登录，提取登录&lt;code&gt;${token}&lt;/code&gt;传入下个接口参数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;添加 HTTP Header Manager：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/9f6015fb46ff4c3eba94cf40c38d5841.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;考场 token，提取考场&lt;code&gt;${exam_token}&lt;/code&gt;传入下个接口参数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;添加 HTTP Header Manager：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4b3a68c97249434aae097fd43cada2f6.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入房间&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;貌似挺合理，HTTP Header Manager 会给下方的接口添加请求头，运行结果真的如我们所料么？&lt;/p&gt;
&lt;h1 id=&#34;运行顺序&#34;&gt;运行顺序&lt;/h1&gt;
&lt;p&gt;在回答这个问题之前，有必要搞懂 JMeter 元件的运行顺序，它是由 2 个维度来决定的：从上往下和元件类型。&lt;/p&gt;
&lt;p&gt;从上往下，即目录树从上往下。元件类型，分为 3 类：&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;/ul&gt;
&lt;p&gt;它们的运行顺序如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;配置元件（如果存在）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;前置处理器（如果存在）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定时器（如果存在）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;取样器（如果存在）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后置处理器（如果存在且取样器的结果不为空）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;断言（如果存在且取样器的结果不为空）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;监听器（如果存在且取样器的结果不为空）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;换句话说，假设我们新建了 1 个线程，想用这个线程去发请求。&lt;/p&gt;
&lt;p&gt;第一步，初始化配置，比如参数化、设置 Header、Cookie 等，用到配置元件。&lt;/p&gt;
&lt;p&gt;第二步，可能需要给线程加点参数，比如用户参数，用到前置处理器。&lt;/p&gt;
&lt;p&gt;第三步，在发送请求前可能会等待一段时间，用到定时器。&lt;/p&gt;
&lt;p&gt;第四步，发送请求，用到取样器。&lt;/p&gt;
&lt;p&gt;第五步，可能需要提取响应数据，比如正则表达式提取器、JMESPath 提取器，用到后置处理器。&lt;/p&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;对于以下所列元件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/107fe872d0c84a08a81dcb909d80a028.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;JMeter 会按以下步骤运行：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;线程组（如果有多个线程组可以在测试计划设置是顺序执行还是同时执行）&lt;/li&gt;
&lt;li&gt;简单控制器（父节点）&lt;/li&gt;
&lt;li&gt;HTTP Cookie 管理器（配置元件）&lt;/li&gt;
&lt;li&gt;用户参数（前置处理器）&lt;/li&gt;
&lt;li&gt;Synchronizing Timer（定时器）&lt;/li&gt;
&lt;li&gt;HTTP 请求 1（取样器）&lt;/li&gt;
&lt;li&gt;正则表达式提取器（后置处理器）&lt;/li&gt;
&lt;li&gt;响应断言（断言）&lt;/li&gt;
&lt;li&gt;HTTP Cookie 管理器（配置元件）&lt;/li&gt;
&lt;li&gt;用户参数（前置处理器）&lt;/li&gt;
&lt;li&gt;Synchronizing Timer（定时器）&lt;/li&gt;
&lt;li&gt;HTTP 请求 2（取样器）&lt;/li&gt;
&lt;li&gt;正则表达式提取器（后置处理器）&lt;/li&gt;
&lt;li&gt;响应断言（断言）&lt;/li&gt;
&lt;li&gt;HTTP 请求 3（取样器）&lt;/li&gt;
&lt;li&gt;察看结果树（严格来讲是与第 6 步并行，也就是取样器之后）&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;作用域&#34;&gt;作用域&lt;/h1&gt;
&lt;p&gt;其中有个观察作用域实际效果的关键元件：HTTP 请求 2，它的前后并没有元件，但是也被作用上了。在 JMeter 中，同一层级的元件具有相同的作用域！&lt;/p&gt;</description>
    </item>
    <item>
      <title>JUnit5 的 Tag、Filter、Order、Lifecycle</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E7%9A%84tagfilterorderlifecycle/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E7%9A%84tagfilterorderlifecycle/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;tag&#34;&gt;Tag&lt;/h1&gt;
&lt;p&gt;JUnit5 可以使用&lt;code&gt;@Tag&lt;/code&gt;注解给测试类和测试方法打 tag，这些 tag 能用来在执行时进行过滤，它跟 group 有点类似。&lt;/p&gt;
&lt;p&gt;tag 应该遵循以下规则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不能为 null 或者为空。&lt;/li&gt;
&lt;li&gt;不能包含空格。&lt;/li&gt;
&lt;li&gt;不能包含 ISO 控制字符。&lt;/li&gt;
&lt;li&gt;不能包含保留字符：&lt;code&gt;,&lt;/code&gt; &lt;code&gt;(&lt;/code&gt; &lt;code&gt;)&lt;/code&gt; &lt;code&gt;&amp;amp;&lt;/code&gt; &lt;code&gt;|&lt;/code&gt; &lt;code&gt;!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&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-java&#34; data-lang=&#34;java&#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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.Tag&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;&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:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;org.junit.jupiter.api.Test&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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;@Tag&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;fast&amp;#34;&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;&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;@Tag&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;model&amp;#34;&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;&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;TaggingDemo&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Test&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;@Tag&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;taxes&amp;#34;&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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;void&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;testingTaxCalculation&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;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#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 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;blockquote&gt;
&lt;p&gt;&lt;code&gt;@Tag&lt;/code&gt;还可以通过元注解和组合注解，实现自定义注解，参考：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://dongfanger.gitee.io/blog/JUnit/002-JUnit5%E6%B3%A8%E8%A7%A3%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%BC%95.html#id2&#34;&gt;https://dongfanger.gitee.io/blog/JUnit/002-JUnit5%E6%B3%A8%E8%A7%A3%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%BC%95.html#id2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;filter&#34;&gt;Filter&lt;/h1&gt;
&lt;p&gt;打好了 tag 后，在执行时可以进行过滤，比如 Maven 配置：&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-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;&amp;lt;!-- ... --&amp;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;&amp;lt;build&amp;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;&amp;lt;plugins&amp;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;&amp;lt;plugin&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-surefire-plugin&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/artifactId&amp;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;&amp;lt;version&amp;gt;&lt;/span&gt;2.22.2&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/version&amp;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;&amp;lt;configuration&amp;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;&amp;lt;groups&amp;gt;&lt;/span&gt;acceptance | !feature-a&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/groups&amp;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;&amp;lt;excludedGroups&amp;gt;&lt;/span&gt;integration, regression&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/excludedGroups&amp;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;&amp;lt;/configuration&amp;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;&amp;lt;/plugin&amp;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;&amp;lt;/plugins&amp;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;&amp;lt;/build&amp;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;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;groups 用来指定包含哪些 tag，excludedGroups 用来指定排除哪些 tag。&lt;/p&gt;</description>
    </item>
    <item>
      <title>pytest 内置和自定义 marker</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E5%86%85%E7%BD%AE%E5%92%8C%E8%87%AA%E5%AE%9A%E4%B9%89marker/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E5%86%85%E7%BD%AE%E5%92%8C%E8%87%AA%E5%AE%9A%E4%B9%89marker/</guid>
      <description>可以通过命令行查看所有 marker，包括内置和自定义的</description>
    </item>
    <item>
      <title>send_keys 报错 element_not_interactable</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/send_keys%E6%8A%A5%E9%94%99element_not_interactable/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/send_keys%E6%8A%A5%E9%94%99element_not_interactable/</guid>
      <description>这两天要在阿里云日志中操作 UI，以输入关键字搜索日志。</description>
    </item>
    <item>
      <title>SpringBoot 接入 JaCoCo 实践教程</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/springboot%E6%8E%A5%E5%85%A5jacoco%E5%AE%9E%E8%B7%B5%E6%95%99%E7%A8%8B/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/springboot%E6%8E%A5%E5%85%A5jacoco%E5%AE%9E%E8%B7%B5%E6%95%99%E7%A8%8B/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;静态接入和动态接入&#34;&gt;静态接入和动态接入&lt;/h2&gt;
&lt;p&gt;SpingBoot 可以通过 2 种方式接入 JaCoCo：Maven 和 Agent。&lt;strong&gt;Maven 方式是静态接入&lt;/strong&gt;，在编译时计算代码覆盖率。&lt;strong&gt;Agent 方式是动态接入&lt;/strong&gt;，服务启起来以后，能实时根据代码命中情况计算代码覆盖率。&lt;/p&gt;
&lt;p&gt;教程代码已上传开源项目：https://github.com/dongfanger/mini&lt;/p&gt;
&lt;h2 id=&#34;maven&#34;&gt;Maven&lt;/h2&gt;
&lt;p&gt;pom.xml 添加依赖：&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-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;dependency&amp;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;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jacoco&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/groupId&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jacoco-maven-plugin&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/artifactId&amp;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;&amp;lt;version&amp;gt;&lt;/span&gt;0.8.7&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/version&amp;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;&amp;lt;/dependency&amp;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;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-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;build&amp;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;&amp;lt;plugins&amp;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;&amp;lt;plugin&amp;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;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/groupId&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-maven-plugin&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/artifactId&amp;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;&amp;lt;version&amp;gt;&lt;/span&gt;${project.parent.version}&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/version&amp;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;&amp;lt;/plugin&amp;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;&amp;lt;plugin&amp;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;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jacoco&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/groupId&amp;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;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jacoco-maven-plugin&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/artifactId&amp;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;&amp;lt;version&amp;gt;&lt;/span&gt;0.8.7&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/version&amp;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;&amp;lt;executions&amp;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;&amp;lt;execution&amp;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;&amp;lt;id&amp;gt;&lt;/span&gt;default-prepare-agent&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/id&amp;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;&amp;lt;goals&amp;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;&amp;lt;goal&amp;gt;&lt;/span&gt;prepare-agent&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/goal&amp;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;&amp;lt;/goals&amp;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;&amp;lt;/execution&amp;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;&amp;lt;execution&amp;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;&amp;lt;id&amp;gt;&lt;/span&gt;default-report&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/id&amp;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;&amp;lt;goals&amp;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;&amp;lt;goal&amp;gt;&lt;/span&gt;report&lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;/goal&amp;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;&amp;lt;/goals&amp;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;&amp;lt;/execution&amp;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;&amp;lt;/executions&amp;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;&amp;lt;/plugin&amp;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;&amp;lt;/plugins&amp;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;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;点击 maven 的 install：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/cf4e615a5587484c82882989c814b72d.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;target/site/jacoco&lt;/code&gt;目录下就生成了覆盖率报告：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/796fd3dead8d4d18aed6fe21d54b3584.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/2ab44387da96478f88ba4ce10204be14.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;agent&#34;&gt;Agent&lt;/h2&gt;
&lt;p&gt;下载 jacoco 文件后解压，提取 2 个文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ad7eed0bddcf4841ae0472bf2d6843e3.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;使用命令启动 SpringBoot，启动参数添加&lt;code&gt;-javaagent&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;java -javaagent:jacoco/jacocoagent.jar -jar target/mini-0.0.1-SNAPSHOT.jar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时会生成 1 个&lt;code&gt;jacoco.exec&lt;/code&gt;文件，这个文件就是覆盖率数据文件，采用以下命令根据数据文件生成 HTML 报告：&lt;/p&gt;</description>
    </item>
    <item>
      <title>把 RPC 包进事务的暗坑</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E6%8A%8Arpc%E5%8C%85%E8%BF%9B%E4%BA%8B%E5%8A%A1%E7%9A%84%E6%9A%97%E5%9D%91/</link>
      <pubDate>Sun, 04 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E6%8A%8Arpc%E5%8C%85%E8%BF%9B%E4%BA%8B%E5%8A%A1%E7%9A%84%E6%9A%97%E5%9D%91/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&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;# 方式：把判断放进 SQL，一条语句的原子性&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rows &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; db&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;execute&lt;span style=&#34;color:#1f2328&#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;    UPDATE pay_record SET status = &amp;#39;PAID&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:#0a3069&#34;&gt;    WHERE order_no = ? AND status = &amp;#39;INIT&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:#0a3069&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; order_no&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;if&lt;/span&gt; rows &lt;span style=&#34;color:#0550ae&#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 style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;已处理&amp;#34;&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;grant_coupon&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;order_no&lt;span style=&#34;color:#1f2328&#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;return&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;grant_coupon 失败会发生什么？&lt;/p&gt;
&lt;p&gt;券永久丢失，该发没发，资损。&lt;/p&gt;
&lt;p&gt;第一次请求：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;UPDATE status: INIT → PAID   ✅ 已提交，改不回去了
grant_coupon(order_no)        ❌ 抛异常 / 超时 / 下游挂了
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第二次请求：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rows = db.execute(&amp;#34;UPDATE ... WHERE status = &amp;#39;INIT&amp;#39;&amp;#34;)  # status已是PAID
# rows == 0
return &amp;#34;已处理&amp;#34;   # ← 被自己的幂等逻辑拦住，不会执行grant_coupon
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;包进一个事务能解决吗&#34;&gt;包进一个事务能解决吗&lt;/h1&gt;
&lt;p&gt;把 update 和 grant_coupon 这两个操作，包进一个事务里，要么同时执行，要么同时回滚，是不是就解决了？&lt;/p&gt;</description>
    </item>
    <item>
      <title>HttpRunner3 的用例是怎么运行起来的</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84%E7%94%A8%E4%BE%8B%E6%98%AF%E6%80%8E%E4%B9%88%E8%BF%90%E8%A1%8C%E8%B5%B7%E6%9D%A5%E7%9A%84/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84%E7%94%A8%E4%BE%8B%E6%98%AF%E6%80%8E%E4%B9%88%E8%BF%90%E8%A1%8C%E8%B5%B7%E6%9D%A5%E7%9A%84/</guid>
      <description>在 PyCharm 中打开&lt;code&gt;examples/httpbin/basic_test.py&lt;/code&gt;：</description>
    </item>
    <item>
      <title>JMeter 英文版界面介绍</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E8%8B%B1%E6%96%87%E7%89%88%E7%95%8C%E9%9D%A2%E4%BB%8B%E7%BB%8D/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E8%8B%B1%E6%96%87%E7%89%88%E7%95%8C%E9%9D%A2%E4%BB%8B%E7%BB%8D/</guid>
      <description>JMeter 的界面虽然不华丽，但是使用起来还是很便利。本文将基于英文版界面对整个可视区域进行介绍，如果想看中文版，请在菜单栏点击 Choose Language 切换：</description>
    </item>
    <item>
      <title>JUnit5 编写基本测试</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E7%BC%96%E5%86%99%E5%9F%BA%E6%9C%AC%E6%B5%8B%E8%AF%95/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E7%BC%96%E5%86%99%E5%9F%BA%E6%9C%AC%E6%B5%8B%E8%AF%95/</guid>
      <description>JUnit5 的测试不是通过名称，而是通过注解来标识的。</description>
    </item>
    <item>
      <title>pytest 精通 fixture</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E7%B2%BE%E9%80%9Afixture/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E7%B2%BE%E9%80%9Afixture/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;fixture-是什么&#34;&gt;fixture 是什么&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;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;fixture&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;    callable_or_scope&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;None&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:#0550ae&#34;&gt;*&lt;/span&gt;args&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;    scope&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;function&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;    params&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;None&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;    autouse&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;False&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;    ids&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;None&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;    name&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;None&lt;/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;&amp;#34;Decorator to mark a fixture factory function.
&lt;/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;    This decorator can be used, with or without parameters, to define a
&lt;/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;    fixture function.
&lt;/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;    The name of the fixture function can later be referenced to cause its
&lt;/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;    invocation ahead of running tests: 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;    modules or classes can use the ``pytest.mark.usefixtures(fixturename)``
&lt;/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;    marker.
&lt;/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;    Test functions can directly use fixture names as input
&lt;/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;    arguments in which case the fixture instance returned from the fixture
&lt;/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;    function will be injected.
&lt;/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;    Fixtures can provide their values to test functions using ``return`` or ``yield``
&lt;/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;    statements. When using ``yield`` the code block after the ``yield`` statement is executed
&lt;/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;    as teardown code regardless of the test outcome, and must yield exactly once.
&lt;/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;    :arg scope: the scope for which this fixture is shared, one of
&lt;/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;function&amp;#34;`` (default), ``&amp;#34;class&amp;#34;``, ``&amp;#34;module&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;package&amp;#34;`` or ``&amp;#34;session&amp;#34;`` (``&amp;#34;package&amp;#34;`` is considered **experimental**
&lt;/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;                at this 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:#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;                This parameter may also be a callable which receives ``(fixture_name, config)``
&lt;/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;                as parameters, and must return a ``str`` with one of the values mentioned above.
&lt;/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;                See :ref:`dynamic scope` in the docs for more information.
&lt;/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;    :arg params: an optional list of parameters which will cause multiple
&lt;/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;                invocations of the fixture function and all of the tests
&lt;/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;                using it.
&lt;/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;                The current parameter is available in ``request.param``.
&lt;/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;    :arg autouse: if True, the fixture func is activated for all tests that
&lt;/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;                can see it.  If False (the default) then an explicit
&lt;/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;                reference is needed to activate the fixture.
&lt;/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;    :arg ids: list of string ids each corresponding to the params
&lt;/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;                so that they are part of the test id. If no ids are provided
&lt;/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;                they will be generated automatically from the params.
&lt;/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;    :arg name: the name of the fixture. This defaults to the name of the
&lt;/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;                decorated function. If a fixture is used in the same module in
&lt;/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;                which it is defined, the function name of the fixture will be
&lt;/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;                shadowed by the function arg that requests the fixture; one way
&lt;/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;                to resolve this is to name the decorated function
&lt;/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;                ``fixture_&amp;lt;fixturename&amp;gt;`` and then use
&lt;/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.fixture(name=&amp;#39;&amp;lt;fixturename&amp;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 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; params &lt;span style=&#34;color:#0550ae&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;None&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:#6639ba&#34;&gt;list&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;params&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;    fixture_function&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; arguments &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; _parse_fixture_args&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;        callable_or_scope&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:#0550ae&#34;&gt;*&lt;/span&gt;args&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;        scope&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;scope&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;params&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;        autouse&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;autouse&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;        ids&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;ids&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;        name&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;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:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; arguments&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;scope&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;    params &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; arguments&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;params&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;    autouse &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; arguments&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;autouse&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;    ids &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; arguments&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;ids&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;    name &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; arguments&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;name&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:#cf222e&#34;&gt;if&lt;/span&gt; fixture_function &lt;span style=&#34;color:#0550ae&#34;&gt;and&lt;/span&gt; params &lt;span style=&#34;color:#0550ae&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;None&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;and&lt;/span&gt; autouse &lt;span style=&#34;color:#0550ae&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;False&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;# direct decoration&lt;/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; FixtureFunctionMarker&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;scope&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; params&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; autouse&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; name&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;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;            fixture_function
&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 style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt; FixtureFunctionMarker&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;scope&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; params&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; autouse&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; ids&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;ids&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; name&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;name&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;</description>
    </item>
    <item>
      <title>webdriver 使用已打开过的 chrome</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/webdriver%E4%BD%BF%E7%94%A8%E5%B7%B2%E6%89%93%E5%BC%80%E8%BF%87%E7%9A%84chrome/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/webdriver%E4%BD%BF%E7%94%A8%E5%B7%B2%E6%89%93%E5%BC%80%E8%BF%87%E7%9A%84chrome/</guid>
      <description>基本功能：</description>
    </item>
    <item>
      <title>代码覆盖率从 0 到 100 的入门实践</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BB%A3%E7%A0%81%E8%A6%86%E7%9B%96%E7%8E%87%E4%BB%8E0%E5%88%B0100%E7%9A%84%E5%85%A5%E9%97%A8%E5%AE%9E%E8%B7%B5/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BB%A3%E7%A0%81%E8%A6%86%E7%9B%96%E7%8E%87%E4%BB%8E0%E5%88%B0100%E7%9A%84%E5%85%A5%E9%97%A8%E5%AE%9E%E8%B7%B5/</guid>
      <description>JaCoCo 全称是&lt;strong&gt;Ja&lt;/strong&gt;va &lt;strong&gt;Co&lt;/strong&gt;de &lt;strong&gt;Co&lt;/strong&gt;verage，Java 代码覆盖率，广泛运用于各种测试平台对 Java 代码的全量覆盖率和增量覆盖率进行统计，分析代码行差异，度量单元测试效果。Jacoco 也是精准测试的技术实现手段之一。</description>
    </item>
    <item>
      <title>基于 HAR 包的流量录制回放</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E5%9F%BA%E4%BA%8Ehar%E5%8C%85%E7%9A%84%E6%B5%81%E9%87%8F%E5%BD%95%E5%88%B6%E5%9B%9E%E6%94%BE/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E5%9F%BA%E4%BA%8Ehar%E5%8C%85%E7%9A%84%E6%B5%81%E9%87%8F%E5%BD%95%E5%88%B6%E5%9B%9E%E6%94%BE/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;什么是-har-包&#34;&gt;什么是 HAR 包？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;HAR（HTTP Archive format）&lt;/code&gt;，是一种&lt;code&gt;JSON&lt;/code&gt;格式的存档格式文件，通用扩展名为 &lt;code&gt;.har&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8f720806d7c44e50810f54a676360e7d.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;HAR 包是 JSON 格式的，打开后，重点关注&lt;code&gt;entries&lt;/code&gt;里面的&lt;code&gt;request&lt;/code&gt;和&lt;code&gt;response&lt;/code&gt;，包含了请求和响应信息。&lt;/p&gt;
&lt;h2 id=&#34;流量录制&#34;&gt;流量录制&lt;/h2&gt;
&lt;p&gt;怎么获取 HAR 包呢？可以网上搜索方法，浏览器 F12、抓包工具（Charles、Proxyman 等）都可以将 HTTP 请求导出为 HAR 包。&lt;/p&gt;
&lt;p&gt;生产级别的流量录制很复杂，想要了解的话可以参考阿里开源项目：https://github.com/alibaba/jvm-sandbox-repeater&lt;/p&gt;
&lt;h2 id=&#34;回放对比&#34;&gt;回放对比&lt;/h2&gt;
&lt;p&gt;本文重点介绍在导出 HAR 包后，怎么通过 Python 来实现回放对比。&lt;/p&gt;
&lt;h3 id=&#34;使用介绍&#34;&gt;使用介绍&lt;/h3&gt;
&lt;p&gt;一、将 HAR 包转换为 pytest 用例&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/fa6977fdad54413b91359c8a37fc111a.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;har_file 为 har 包路径，profile 配置开启回放，调用 Har.har2case()方法将 HAR 包转换为 pytest 用例。&lt;/p&gt;
&lt;p&gt;转换后会生成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;demo_test.py 与 HAR 同名的 pytest 用例文件&lt;/li&gt;
&lt;li&gt;demo-replay-diff 对比结果目录，暂时为空&lt;/li&gt;
&lt;li&gt;sqlite.db 存储 HAR 包响应数据，标记为 expect&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;二、执行 pytest 用例&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.libraries.Diff&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; Diff
&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.libraries.Sqlite&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; Sqlite
&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;HTTPRequestKeyword&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; BodyKeyword&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;    var &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;caseId&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;023f387db398504889e0afb8dd1bc99d&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;requestOrder&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#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;#39;diffDir&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;/Users/wanggang424/Desktop/PycharmProjects/tep/tests/demo/case/har/demo-replay-diff&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;    url &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;https://postman-echo.com/get?foo1=HDnY8&amp;amp;foo2=34.5&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;#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;postman-echo.com&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;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;HttpRunnerPlus&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&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;    ro &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; HTTPRequestKeyword&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;
&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;# user_defined_var = ro.response.jsonpath(&amp;#34;$.jsonpath&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;assert&lt;/span&gt; ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&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;    Sqlite&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;record_actual&lt;span style=&#34;color:#1f2328&#34;&gt;((&lt;/span&gt;ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;text&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;caseId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;],&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;requestOrder&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;GET&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://postman-echo.com/get?foo1=HDnY8&amp;amp;foo2=34.5&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;),&lt;/span&gt; var&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; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;https://postman-echo.com/post&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;#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;postman-echo.com&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;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;Go-http-client/1.1&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;Content-Length&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;28&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;Content-Type&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; charset=UTF-8&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;Cookie&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;sails.sid=s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk&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&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;sails.sid&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;s%3Az_LpglkKxTvJ_eHVUH6V67drKp0AGWW-.PidabaXOnatLRP47hVyqqepl6BdrpEQzRlJQXtbIiwk&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;    body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&amp;#34;{&amp;#34;foo1&amp;#34;:&amp;#34;HDnY8&amp;#34;,&amp;#34;foo2&amp;#34;:12.3}&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;    ro &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; BodyKeyword&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:#57606a&#34;&gt;# ro = BodyKeyword(body, {&amp;#34;$.jsonpath&amp;#34;: &amp;#34;value&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; ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ro &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; HTTPRequestKeyword&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:#57606a&#34;&gt;# user_defined_var = ro.response.jsonpath(&amp;#34;$.jsonpath&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;assert&lt;/span&gt; ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&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;    Sqlite&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;record_actual&lt;span style=&#34;color:#1f2328&#34;&gt;((&lt;/span&gt;ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;text&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;caseId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;],&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;requestOrder&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;POST&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://postman-echo.com/post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;),&lt;/span&gt; var&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; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;https://postman-echo.com/post&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;#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;postman-echo.com&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;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;Go-http-client/1.1&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;Content-Length&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;20&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;Content-Type&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/x-www-form-urlencoded; charset=UTF-8&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;Cookie&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;sails.sid=s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;%2F&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;Aw&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&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;sails.sid&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;s%3AS5e7w0zQ0xAsCwh9L8T6R7QLYCO7_gtD.r8%2B2w9IWqEIfuVkrZjnxzm2xADIk34zKAWXRPapr&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;%2F&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;Aw&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;    ro &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; HTTPRequestKeyword&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; data&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:#57606a&#34;&gt;# user_defined_var = ro.response.jsonpath(&amp;#34;$.jsonpath&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;assert&lt;/span&gt; ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&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;    Sqlite&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;record_actual&lt;span style=&#34;color:#1f2328&#34;&gt;((&lt;/span&gt;ro&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;response&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;text&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;caseId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;],&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;requestOrder&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;POST&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://postman-echo.com/post&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;),&lt;/span&gt; var&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;    Diff&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;make&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;caseId&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;],&lt;/span&gt; var&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;diffDir&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;执行时会记录实际请求响应，存入 sqlite 数据库，标记为 actual。&lt;/p&gt;</description>
    </item>
    <item>
      <title>幂等测试</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E5%B9%82%E7%AD%89%E6%B5%8B%E8%AF%95/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E5%B9%82%E7%AD%89%E6%B5%8B%E8%AF%95/</guid>
      <description>连续调用两次，第一次提交成功，第二次会被驳回——串行重复，系统是防住了的。</description>
    </item>
    <item>
      <title>Android 测试三件套：传文件、抓包、看日志</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/android%E6%B5%8B%E8%AF%95%E4%B8%89%E4%BB%B6%E5%A5%97%E4%BC%A0%E6%96%87%E4%BB%B6%E6%8A%93%E5%8C%85%E7%9C%8B%E6%97%A5%E5%BF%97/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/android%E6%B5%8B%E8%AF%95%E4%B8%89%E4%BB%B6%E5%A5%97%E4%BC%A0%E6%96%87%E4%BB%B6%E6%8A%93%E5%8C%85%E7%9C%8B%E6%97%A5%E5%BF%97/</guid>
      <description>在对安卓进行测试时，我们需要把 apk 传到安卓机上，对请求抓包，同时监控应用日志。本文就来讲讲具体操作。</description>
    </item>
    <item>
      <title>HttpRunner3 的 HTTP 请求是怎么发出去的</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84http%E8%AF%B7%E6%B1%82%E6%98%AF%E6%80%8E%E4%B9%88%E5%8F%91%E5%87%BA%E5%8E%BB%E7%9A%84/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E7%9A%84http%E8%AF%B7%E6%B1%82%E6%98%AF%E6%80%8E%E4%B9%88%E5%8F%91%E5%87%BA%E5%8E%BB%E7%9A%84/</guid>
      <description>在 HttpRunner3 的示例代码中，发送 HTTP 请求的代码是这样写的：</description>
    </item>
    <item>
      <title>JMeter 是如何运行施压的</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E6%98%AF%E5%A6%82%E4%BD%95%E8%BF%90%E8%A1%8C%E6%96%BD%E5%8E%8B%E7%9A%84/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E6%98%AF%E5%A6%82%E4%BD%95%E8%BF%90%E8%A1%8C%E6%96%BD%E5%8E%8B%E7%9A%84/</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;JMeter 下载地址为：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://jmeter.apache.org/download_jmeter.cgi&#34;&gt;https://jmeter.apache.org/download_jmeter.cgi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最新版本为 5.4.1，依赖于 Java8+，下载后无需安装，解压即用。解压后的目录和文件如下：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/8882ce2b3e0f46aab689527e19940f07.png&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bin 二进制文件目录，包括配置文件、启动文件、启动 jar 包、示例脚本。比如 Windows 和 Linux 系统的两个启动脚本：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/90a79cc617a44ecb991bec7a4c75c2cf.png&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;docs API 离线帮助文档。&lt;/li&gt;
&lt;li&gt;extras 辅助功能，可用于 Ant、Jenkins 集成。&lt;/li&gt;
&lt;li&gt;lib lib 根目录下存放 JMeter 基础 jar 包，lib/ext 目录下存放第三方或自定义扩展 jar 包。&lt;/li&gt;
&lt;li&gt;licenses 证书文本文件。&lt;/li&gt;
&lt;li&gt;printable_docs 离线帮助文档。&lt;/li&gt;
&lt;li&gt;LICENSE 证书说明。&lt;/li&gt;
&lt;li&gt;NOTICE 版权声明。&lt;/li&gt;
&lt;li&gt;README 简介。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;测试计划&#34;&gt;测试计划&lt;/h1&gt;
&lt;p&gt;JMeter 以图形界面方式启动后，首先映入眼帘的是左侧的 TestPlan，测试计划：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/08391c0919354b15aaf9807d7d60e07f.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;JMeter 的 UI 界面只能同时编辑一个测试计划，如果想再编辑另外一个测试计划，必须再打开一个 JMeter 界面。这是因为测试计划对应的是.jmx 文件！不同的.jmx 文件只能在不同的界面中编辑。&lt;/p&gt;
&lt;p&gt;JMeter 界面是通过组件拖拽的方式来编写脚本的，脚本的根目录是测试计划。根节点只能有一个，作为根节点，测试计划很适合来做全局配置，比如在 User Defined Variables 处添加键值对格式的全局变量。&lt;/p&gt;
&lt;p&gt;测试计划是默认节点，JMeter 还有另外一个不可缺少的次节点：Thread Group，线程组。&lt;/p&gt;
&lt;h1 id=&#34;多线程&#34;&gt;多线程&lt;/h1&gt;
&lt;p&gt;性能测试工具的目的是用少量电脑产生成百上千个并发请求。多进程和多线程正好能用来实现这个需求，书上有句关于进程和线程的至理名言：“进程是操作系统分配资源的最小单位，线程是程序执行的最小单位”。该如何理解呢？前一句的意思是说，每个应用程序都有对应的进程，按照进程来分配资源，可以使得资源利用最大化，避免浪费。后一句的理解得从一个故事讲起，很久以前，进程拿着资源找 CPU 执行。CPU 一看进程太大，说我处理不了。进程只能拆成多个线程给 CPU，不但能执行了，还能在线程之间灵活切换。&lt;/p&gt;</description>
    </item>
    <item>
      <title>JUnit5 注解学习指引</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E6%B3%A8%E8%A7%A3%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%BC%95/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/junit5%E6%B3%A8%E8%A7%A3%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%BC%95/</guid>
      <description>注解（Annotations）是 JUnit 的标志性技术，本文就来对它的 20 个注解，以及元注解和组合注解进行学习。</description>
    </item>
    <item>
      <title>Postman 竟然还能录制流量</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/postman%E7%AB%9F%E7%84%B6%E8%BF%98%E8%83%BD%E5%BD%95%E5%88%B6%E6%B5%81%E9%87%8F/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/postman%E7%AB%9F%E7%84%B6%E8%BF%98%E8%83%BD%E5%BD%95%E5%88%B6%E6%B5%81%E9%87%8F/</guid>
      <description>大家好，我是刚哥。</description>
    </item>
    <item>
      <title>pytest132 个命令行参数用法</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest132%E4%B8%AA%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E7%94%A8%E6%B3%95/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest132%E4%B8%AA%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E7%94%A8%E6%B3%95/</guid>
      <description>在 Shell 执行&lt;code&gt;pytest -h&lt;/code&gt;可以看到 pytest 的命令行参数有这 10 大类，共 132 个</description>
    </item>
    <item>
      <title>代码评审，揭示黑盒背后的真相</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BB%A3%E7%A0%81%E8%AF%84%E5%AE%A1%E6%8F%AD%E7%A4%BA%E9%BB%91%E7%9B%92%E8%83%8C%E5%90%8E%E7%9A%84%E7%9C%9F%E7%9B%B8/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BB%A3%E7%A0%81%E8%AF%84%E5%AE%A1%E6%8F%AD%E7%A4%BA%E9%BB%91%E7%9B%92%E8%83%8C%E5%90%8E%E7%9A%84%E7%9C%9F%E7%9B%B8/</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;/p&gt;
&lt;p&gt;&amp;ldquo;They think I am hiding in the shadows, but I am the shadows.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/4ff9ff50f1044666a546f23760a2e663.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/f8d7e7c0dd1d46c2874552514624fed8.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;黑盒测试存在一些局限性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;可能无法发现与系统实现相关的问题&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可能无法覆盖所有的测试场景&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;测试效率较低，比如准备物料、模拟场景&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;强依赖需求文档，如果文档不全，测试会漏&lt;/p&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;代码评审，Code Review（CR），是一种通过检查代码来提高代码质量的过程。&lt;/p&gt;
&lt;p&gt;对于测试人员来说，参与代码评审，可以尽量提前发现问题，减少修复代价，提高效能。&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;/p&gt;
&lt;p&gt;&lt;strong&gt;Code Diff&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;查看 Code Diff，可以借助 Gitlab 或 IDEA，比较分支差异或版本差异。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/b9ae62348bf4455c93d6a0e2f42f8bd8.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;对比时机：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;提测前和测试中，自行走查代码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;发现缺陷，定位代码原因&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修复缺陷后，评估影响范围&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;上线前，是否夹带代码&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;精准测试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;评估测试用例的代码覆盖率，查漏补缺，Jacoco 的 on-the-fly 模式支持动态收集代码覆盖率数据。&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;p&gt;在做 Code Diff 时，也没必要把每个文件、每行代码的意思搞懂，比如研发对代码结构做了调整，在 diff 时要梳理清楚的话，ROI 会非常低，因为既消耗时间，又发现不了问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;那该怎么做代码评审呢？&lt;/strong&gt; 关注业务：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;跟需求文档比较，哪些需求是遗漏的，哪些代码是补充的，哪些代码是夹带的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关注核心业务代码逻辑，使用条件覆盖、路径覆盖等方法设计测试用例&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优化测试用例，针对代码实现考虑异常、边界、幂等、并发等场景&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;代码评审要求测试人员具备代码能力，理解编程语言，掌握软件设计，熟悉代码结构和架构，多与开发同学交流，共同优化代码质量。&lt;/p&gt;
&lt;h1 id=&#34;六代码评审的实际案例&#34;&gt;六、代码评审的实际案例&lt;/h1&gt;
&lt;p&gt;1、空指针异常&lt;/p&gt;</description>
    </item>
    <item>
      <title>接口设计的可测试性</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1%E7%9A%84%E5%8F%AF%E6%B5%8B%E8%AF%95%E6%80%A7/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E6%8E%A5%E5%8F%A3%E8%AE%BE%E8%AE%A1%E7%9A%84%E5%8F%AF%E6%B5%8B%E8%AF%95%E6%80%A7/</guid>
      <description>错误码不分层的接口，你只能靠 msg 断言，改个文案用例就跑失败。返回快照的字段返回了实时，数据就无法核对上。</description>
    </item>
    <item>
      <title>理想中的接口自动化项目</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E7%90%86%E6%83%B3%E4%B8%AD%E7%9A%84%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96%E9%A1%B9%E7%9B%AE/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E7%90%86%E6%83%B3%E4%B8%AD%E7%9A%84%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96%E9%A1%B9%E7%9B%AE/</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;我理想中的接口自动化项目，是有一套脚本，能够 100%覆盖核心业务场景，能够回归线上频繁出现的问题，能够支持手工无法验证的场景，能够抛出偶现问题，能够包含容易遗漏的边界场景、异常场景，每天都能定时运行，出具报告，给予反馈。&lt;/p&gt;
&lt;p&gt;用实际的&lt;strong&gt;指标量化&lt;/strong&gt;下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能测试自动化率 = 自动化测试用例数/总测试用例数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自动化发现 bug 占比 = 自动化发现的 bug/总 bug 数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自动化发现 bug 有效性 = 自动化发现的有效 bug/自动化提交的 bug 总数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自动化执行成功率 = 自动化执行成功的次数/自动化执行总次数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;周活跃自动化用例占比 = (∑每周执行过的自动化测试用例)/(∑周维度自动化测试用例)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;应用维度自动化代码覆盖率 = 自动化用例覆盖代码行数/应用维度代码总行数&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;现状是什么situation&#34;&gt;现状是什么(Situation)&lt;/h1&gt;
&lt;p&gt;我负责的业务是发票中台，数据流简化后如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/b337c73fa45a4a5b80cc91b13305c987.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;发票中台对调用方提供 RPC 服务，底层有读写 MySQL、同步 ES、消息队列、异步任务等服务，上游是用户和订单，需要账号、增票资质、订单等物料；下游是财务，推送申请单给财务开具发票等。&lt;/p&gt;
&lt;p&gt;发票的接口自动化在多年前是写了的，用 Java+TestNG+RPC 本地化了一套框架来实现，因为人员离职和业务变更，这套自动化到我手上的时候，已经不能用了，本地化的接口自动化项目也无法做数据度量，只有接口测试平台写的自动化用例才会被统计到。&lt;/p&gt;
&lt;h1 id=&#34;任务是什么task&#34;&gt;任务是什么(Task)&lt;/h1&gt;
&lt;p&gt;我给自己定的绩效是在 Q2 完成 95 条自动化用例，按照一个标准化的自动化项目来实现发票的接口自动化，结合测试环境、测试物料、流水线、精准分析，打造一个个人的稳定的沉淀物，无论是晋升还是求职，都能拿来讲点故事。&lt;/p&gt;
&lt;h1 id=&#34;行动是什么action&#34;&gt;行动是什么(Action)&lt;/h1&gt;
&lt;h2 id=&#34;编写自动化用例的方式&#34;&gt;&lt;strong&gt;编写自动化用例的方式&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;1、最理想的方式，是能够录制线上流量直接转换为自动化用例，修修改改即可。虽然我们有流量录制回放平台，但是它跟接口自动化平台并没有打通，很可惜不能采取这种方式。&lt;/p&gt;
&lt;p&gt;2、参考线上流量日志，抓取接口、入参，人工转化为自动化用例。&lt;/p&gt;
&lt;p&gt;3、参考接口的代码设计，比如 controller/provider/sdk 都定义了应用接口，能知道哪些覆盖到了，哪些还需要补充。&lt;/p&gt;
&lt;p&gt;4、根据入参模板自行构造参数。参数个数太多的话造起来很麻烦。&lt;/p&gt;
&lt;p&gt;我主要采用第 2 种和第 3 种方式结合来编写自动化用例。&lt;/p&gt;
&lt;h2 id=&#34;典型自动化用例&#34;&gt;&lt;strong&gt;典型自动化用例&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/5f98cecbf9624b92b0947b266bf882e3.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;前置步骤：初始化数据，将订单发票类型更新为 3。&lt;/p&gt;
&lt;p&gt;预设变量：提供参数化数据。&lt;/p&gt;
&lt;p&gt;执行步骤：调用修改订单发票类型接口，将发票类型 3 改为 2，入参引用预设变量。&lt;/p&gt;</description>
    </item>
    <item>
      <title>CodeReview 技巧和规范</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/codereview%E6%8A%80%E5%B7%A7%E5%92%8C%E8%A7%84%E8%8C%83/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/codereview%E6%8A%80%E5%B7%A7%E5%92%8C%E8%A7%84%E8%8C%83/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;what-什么是-codereview&#34;&gt;What-什么是 CodeReview？&lt;/h1&gt;
&lt;p&gt;CodeReview，即代码评审，是白盒测试中，静态测试的一种方法，通过阅读代码，提出代码层面的缺陷。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/58e1d84948084828a44a609553cbbe3f.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;why-为什么做-codereview&#34;&gt;Why-为什么做 CodeReview？&lt;/h1&gt;
&lt;p&gt;测试左移：在测试前就发现代码缺陷。&lt;/p&gt;
&lt;p&gt;明确测试范围：根据需求文档和代码，对齐测试范围，防止研发夹带本次需求范围外的代码上线。&lt;/p&gt;
&lt;p&gt;提升代码能力：学习研发优秀的编程技术。&lt;/p&gt;
&lt;p&gt;深入原理：了解系统内部结构及实现。&lt;/p&gt;
&lt;p&gt;发现隐藏缺陷：能发现某些极端场景下的问题或概率性问题。&lt;/p&gt;
&lt;p&gt;减少测试研发周期占比：节省用例执行个数，尤其多个同类异常梳理，不用执行类似用例，CodeReview 一行代码即可。&lt;/p&gt;
&lt;h1 id=&#34;who-谁适合做-codereview&#34;&gt;Who-谁适合做 CodeReview？&lt;/h1&gt;
&lt;p&gt;CodeReview 需要三大门槛：&lt;/p&gt;
&lt;p&gt;①熟悉编程语言，Java/Python/Go 等。&lt;/p&gt;
&lt;p&gt;②熟悉编程规范，《阿里巴巴 XXX 编码规范》、《华为 XXX 编码规范》、《京东 XXX 编码规范》。&lt;/p&gt;
&lt;p&gt;③了解需求、架构和设计，需求文档/架构图/设计文档。&lt;/p&gt;
&lt;p&gt;达到门槛才适合做 CodeReview。&lt;/p&gt;
&lt;h1 id=&#34;when-什么时候做-codereview&#34;&gt;When-什么时候做 CodeReview？&lt;/h1&gt;
&lt;p&gt;提测前，通过 Code Diff Review，自己走读代码或者参与研发代码评审会议均可，&lt;/p&gt;
&lt;p&gt;发现功能缺陷时，通过走查代码定位代码原因，并分析是否有关联缺陷。&lt;/p&gt;
&lt;p&gt;修复缺陷时，阅读研发 commit 记录，检查是否引入新缺陷。&lt;/p&gt;
&lt;p&gt;上线前，通过 merge 范围检查是否有夹带代码，为了测试改动的临时代码是否恢复。&lt;/p&gt;
&lt;h1 id=&#34;where-在哪里做-codereview&#34;&gt;Where-在哪里做 CodeReview？&lt;/h1&gt;
&lt;p&gt;线上仓库（如 Gitlab），适合于修改点单一，不需要查看上下文信息就能找到缺陷。直接在网页端评审代码且发表 comment。&lt;/p&gt;
&lt;p&gt;本地仓库（如 IDEA），适合于修改点较多，需要查看上下文信息（变量或方法跳转到定义或使用之处）。在本地找到缺陷后在线上发表 comment。&lt;/p&gt;
&lt;h1 id=&#34;how-codereview-如何上手&#34;&gt;How-CodeReview 如何上手？&lt;/h1&gt;
&lt;p&gt;准备：了解需求、架构和设计；学习编程语言；学习代码规范；&lt;/p&gt;
&lt;p&gt;实践：坚持 CodeReview，记录缺陷，定期总结。如果看代码找缺陷比较困难，可以先从发现功能缺陷后，定位代码原因入手。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见 CR 缺陷：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;除数为 0、整数溢出、精度损失；&lt;/p&gt;
&lt;p&gt;可能死循环；&lt;/p&gt;
&lt;p&gt;在 finally 程序块中关闭或者释放资源；&lt;/p&gt;
&lt;p&gt;异常未处理或提示不明确（没有 catch 异常，集合等没有判空和长度为 0）；&lt;/p&gt;
&lt;p&gt;公式计算错误；&lt;/p&gt;</description>
    </item>
    <item>
      <title>HttpRunner3 源码剖析__init__文件干了些啥</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90__init__%E6%96%87%E4%BB%B6%E5%B9%B2%E4%BA%86%E4%BA%9B%E5%95%A5/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/httprunner3%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90__init__%E6%96%87%E4%BB%B6%E5%B9%B2%E4%BA%86%E4%BA%9B%E5%95%A5/</guid>
      <description>大家好，我是刚哥。</description>
    </item>
    <item>
      <title>Java 新一代单元测试框架 JUnit5 速览</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/java%E6%96%B0%E4%B8%80%E4%BB%A3%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6junit5%E9%80%9F%E8%A7%88/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/java%E6%96%B0%E4%B8%80%E4%BB%A3%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6junit5%E9%80%9F%E8%A7%88/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;为什么学-junit5&#34;&gt;为什么学 JUnit5&lt;/h1&gt;
&lt;p&gt;Java 技术栈的单元测试框架有两个：JUnit 和 TestNG，有种说法是 TestNG 比 JUnit 更强大，学 TestNG 就够了，但是当我打开 GitHub 看到 star 的时候，犹豫了：&lt;/p&gt;
&lt;p&gt;JUnit&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/ed3f70478ba3485b8f7640107f425fe7.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;TestNG&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d46a649ad5cb4a6b999f0cf96ea846bb.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;相差了足足有&lt;strong&gt;3K&lt;/strong&gt;之多。带着这个困惑，我在网上查阅了一番资料，原来&lt;strong&gt;JUnit5 相较于 JUnit4 有了重大升级，已经包含了 TestNG 的所有功能&lt;/strong&gt;。为了坚定我学 JUnit 的想法，我咨询了身边做 Java 开发的朋友，&lt;strong&gt;开发写 UT 都是用的 JUnit&lt;/strong&gt;。这两个理由足以让我开始对 JUnit5 的学习。&lt;/p&gt;
&lt;h1 id=&#34;junit5-是什么&#34;&gt;JUnit5 是什么&lt;/h1&gt;
&lt;p&gt;跟之前的 JUnit 有所不同，JUnit5 由三个子项目的模块组成：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;JUnit5 = JUnit Platform + JUnit Jupiter + JUnit Vintage&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JUnit Platform&lt;/strong&gt; 在 JVM 上启动测试框架的基础，不仅支持 Junit 自制的测试引擎，其他测试引擎也都可以接入。提供了从命令行启动平台的 Console Launcher，和支持 JUnit4 环境的 JUnit4 based Runner。IDE 一般都集成了 JUnit Platform，比如 IntelliJ IDEA。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JUnit Jupiter&lt;/strong&gt; JUnit5 新特性，结合了新的 programming model 和 extension model，以便于在 JUnit5 中编写测试和扩展。提供了在平台上运行基于 Jupiter 测试的 TestEngine。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JUnit Vintage&lt;/strong&gt; 提供了在平台上运行基于 JUnit3 和 JUnit4 测试的 TestEngine，照顾老的项目。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;junit5-新特性&#34;&gt;JUnit5 新特性&lt;/h1&gt;
&lt;h2 id=&#34;使用-junit-jupiter-写测试&#34;&gt;使用 JUnit Jupiter 写测试&lt;/h2&gt;
&lt;p&gt;示例代码：&lt;/p&gt;</description>
    </item>
    <item>
      <title>java&#43;appium&#43;安卓模拟器实现 app 自动化 Demo</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/java&#43;appium&#43;%E5%AE%89%E5%8D%93%E6%A8%A1%E6%8B%9F%E5%99%A8%E5%AE%9E%E7%8E%B0app%E8%87%AA%E5%8A%A8%E5%8C%96demo/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/java&#43;appium&#43;%E5%AE%89%E5%8D%93%E6%A8%A1%E6%8B%9F%E5%99%A8%E5%AE%9E%E7%8E%B0app%E8%87%AA%E5%8A%A8%E5%8C%96demo/</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;/p&gt;
&lt;h2 id=&#34;nodejs&#34;&gt;node.js&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://nodejs.org/en/download/&#34;&gt;https://nodejs.org/en/download/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;cmd 输入 node -v，出现下图说明成功：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/9a8a97cbb71242eeba95dfda59f7d141.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;jdk&#34;&gt;JDK&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&#34;&gt;https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;百度一下“配置 Java”，配置环境变量。&lt;/p&gt;
&lt;p&gt;cmd 输入 java -version，出现下图说明成功：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/147d699f24154e4cbdb6c3da1c03b5fa.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;android-sdk&#34;&gt;Android SDK&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.androiddevtools.cn/&#34;&gt;https://www.androiddevtools.cn/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/7611fee79e654c34842a7d9877ba3f51.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/0e592229d5de460caf92ccde1004760c.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;百度一下&amp;quot;配置 Android SDK 环境变量&amp;quot;。&lt;/p&gt;
&lt;p&gt;cmd 输入 adb，出现下图说明成功：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/aa74daf57c50425a98dbd916d1a1cea0.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;appium-desktop&#34;&gt;Appium Desktop&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/appium/appium-desktop/releases&#34;&gt;https://github.com/appium/appium-desktop/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;或&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://testerhome.com/topics/680&#34;&gt;https://testerhome.com/topics/680&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/bc6783ed94584136bbb7bec00ecb67e1.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/d335ce8b69474881ba5a09e3c9aa880a.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;maven&#34;&gt;Maven&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://maven.apache.org/download.cgi&#34;&gt;http://maven.apache.org/download.cgi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;百度一下“ maven 环境配置”。&lt;/p&gt;
&lt;p&gt;cmd 输入 mvn -version，出现下图说明成功：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/2b4136dfdb6b45d3a33061601eacd9d4.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;intellij-idea&#34;&gt;IntelliJ IDEA&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.jetbrains.com/idea/&#34;&gt;http://www.jetbrains.com/idea/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;安卓模拟器&#34;&gt;安卓模拟器&lt;/h2&gt;
&lt;p&gt;下载地址：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.yeshen.com/&#34;&gt;https://www.yeshen.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;把 Android SDK 目录下 &lt;code&gt;\platform-tool&lt;/code&gt; 的 adb.exe 拷贝。&lt;/p&gt;
&lt;p&gt;打开 &lt;code&gt;Nox\bin&lt;/code&gt; 目录，粘贴替换 adb.exe。&lt;/p&gt;</description>
    </item>
    <item>
      <title>JMeter 全部组件概览</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%85%A8%E9%83%A8%E7%BB%84%E4%BB%B6%E6%A6%82%E8%A7%88/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/jmeter%E5%85%A8%E9%83%A8%E7%BB%84%E4%BB%B6%E6%A6%82%E8%A7%88/</guid>
      <description>JMeter 是性能测试工具，在使用 JMeter 变多以后，越来越觉得之所以它能成为当今时代最主流测试工具，是因为它太成熟了，举个小例子，编写 GET 请求的时候，需要把查询参数，也就是?号后面的请求参数添加进去，JMeter 竟然有个 Add from Clipboard 功能，在浏览器复制以后，点这个按钮就填充好了:</description>
    </item>
    <item>
      <title>pytest 快速入门</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/pytest%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;安装-python-和编辑器-pycharm&#34;&gt;安装 Python 和编辑器 PyCharm&lt;/h2&gt;
&lt;p&gt;Python 安装包下载地址是：
&lt;a href=&#34;https://www.python.org/downloads/&#34;&gt;https://www.python.org/downloads/&lt;/a&gt;
PyCharm 安装包下载地址是：
&lt;a href=&#34;https://www.jetbrains.com/pycharm/download/&#34;&gt;https://www.jetbrains.com/pycharm/download/&lt;/a&gt;
下载后按常规软件安装。&lt;/p&gt;
&lt;h2 id=&#34;pytest&#34;&gt;pytest&lt;/h2&gt;
&lt;p&gt;pytest 是 Python 内置包，无需单独安装，但是&lt;strong&gt;需要设置&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;创建 1 个 Project 和 1 个 test.py 来试试，左上角点击 File | New Project | create，创建项目。在项目中右键选择 New | File 创建 test.py&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/04e917d2accb49c898675998ef16712f.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/7abfb30af4024c75bd0b31291206334d.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/741e459b613d48e290763371ba172eef.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;接着设置 PyCharm，默认 PyCharm 自带的测试框架是 Unittests，在 File | Settings | Tools | Python Integrated Tools | Testing | Default test runner，修改为 pytest&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/183120bd3788416d8e9e89c26b5e9f36.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;在 test.py 文件中添加代码&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;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;    &lt;span style=&#34;color:#cf222e&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个时候 def 的左边会出现一个绿色的箭头，点击之后就可以使用 pytest 运行，注意一定要是 &lt;strong&gt;pytest for&amp;hellip;&lt;/strong&gt; 才可以，如果只是 Run test.py，是不走 pytest 执行的（右上角 Edit Configurations 删掉来解决）&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零开始学 mitmproxy 抓包工具</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6mitmproxy%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E5%AD%A6mitmproxy%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;man-in-the-middle&#34;&gt;Man In The Middle&lt;/h2&gt;
&lt;p&gt;mitm 是 Man In The Middle 的首字母缩写，意思是位于中间的人，表明 mitmproxy 是一个代理，可以拦截请求，实现网络抓包。知名的网络抓包工具有 Fiddler、Charles、HttpWatch、WireShark、BurpSuite 和 Postman 等，mitmproxy 相对来说，没有这么高的知名度，它相比于以上工具的独特优势是，它提供了 Python API，可以编写 Python 代码对网络请求进行流量录制，从而收集接口信息以及转化为接口自动化用例等。mitmproxy 官方文档不但有 mitmproxy 工具使用介绍，还有代理实现原理，对我们学习掌握网络知识有很大帮助。&lt;/p&gt;
&lt;h2 id=&#34;安装与使用&#34;&gt;安装与使用&lt;/h2&gt;
&lt;h3 id=&#34;pip-安装&#34;&gt;pip 安装&lt;/h3&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 mitmproxy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;mitmproxy 是 Python 开发的，用 pip 就能安装成功。官方给出了二进制安装包等方法，我觉得作为一款用 Python 开发的工具，用 pip 直接安装是更 Pythonic 的。&lt;/p&gt;
&lt;h3 id=&#34;命令启动&#34;&gt;命令启动&lt;/h3&gt;
&lt;p&gt;可以通过 3 种命令启动 mitmproxy：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;mitmproxy 控制台&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/98b5387530e44e849f29b5e0a8d4aee3.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;mitmweb 网页&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/758ae8bcd1b54468a460577672942bc9.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;mitmdump 命令行&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它们的内容都是一样的，只是 UI 不一样而已。&lt;/p&gt;
&lt;h3 id=&#34;手动设置代理&#34;&gt;手动设置代理&lt;/h3&gt;
&lt;p&gt;打开以后 mitmproxy 默认会监听http://localhost:8080，需要设置下代理，把代理服务器设置为这个地址。&lt;/p&gt;
&lt;p&gt;Windows 可以这样设置：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;../assets/aa92b1c024054923928528b49e0a1c0e.png&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;设置后一定要记得点击保存按钮，否则代理不会生效。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实际上，Charles 等网络抓包工具会自动配置代理，而 mitmproxy 需要手动设置代理，开发团队的解释是因为浏览器版本和配置经常会变化，所以他们建议在网上根据环境搜索方法手动设置。&lt;/p&gt;</description>
    </item>
    <item>
      <title>一个请求的一生</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%B8%80%E4%B8%AA%E8%AF%B7%E6%B1%82%E7%9A%84%E4%B8%80%E7%94%9F/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%B8%80%E4%B8%AA%E8%AF%B7%E6%B1%82%E7%9A%84%E4%B8%80%E7%94%9F/</guid>
      <description>点进 Service，全是接口。点进 ServiceImpl，前半段在校验业务规则，后半段调了个 stockService.deduct()，又得跳到另一个 Service。</description>
    </item>
    <item>
      <title>中台 RPC 接口自动化怎么做</title>
      <link>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%B8%AD%E5%8F%B0rpc%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96%E6%80%8E%E4%B9%88%E5%81%9A/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://dongfanger.github.io/posts/%E6%B5%8B%E8%AF%95/%E4%B8%AD%E5%8F%B0rpc%E6%8E%A5%E5%8F%A3%E8%87%AA%E5%8A%A8%E5%8C%96%E6%80%8E%E4%B9%88%E5%81%9A/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;本书作者：测试开发刚哥，文章版权归个人所有，未经授权请勿转载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;rpc-接口&#34;&gt;RPC 接口&lt;/h1&gt;
&lt;p&gt;我所在的部门是技术中台，主要负责 toB 业务，上游是业务条线，包括 PC、APP、小程序等终端，这些端的后台是 HTTP 接口层，而在 HTTP 接口层之下，就是技术中台的 RPC 接口层。&lt;/p&gt;
&lt;p&gt;HTTP 是一种超文本传输协议，基于 TCP/IP 来传输文本、图片、音频、视频等数据。数据包的传输能力不是 HTTP 提供的，而是 TCP/IP 干的。&lt;strong&gt;HTTP 的本质是客户端和服务端约定好的一种通信格式，是一种全世界认可的通用范本。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RPC 是一种远程调用，也就是进程间通信，既可以用 HTTP 协议，也可以用自定义协议。&lt;strong&gt;RPC 的好处是没有 HTTP 那么多约束，简化了写法，可以根据公司自定义规范，内部统一即可。&lt;/strong&gt; 所以在公司内部特别流行，比如阿里的 Dubbo 接口就是一种 RPC 接口，我们公司用的 JSF 接口，自研的服务框架，简称杰夫。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;谈谈京东的服务框架 &lt;a href=&#34;https://juejin.cn/post/6948351920088186888&#34;&gt;https://juejin.cn/post/6948351920088186888&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;User&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;getUserById&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Long&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;       &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;userDao&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;getUserById&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;id&lt;span style=&#34;color:#1f2328&#34;&gt;);&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// 这叫本地调用&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &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;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而 RPC 远程调用，调用的是另外一个服务的方法：&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;User&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;getUserById&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;Long&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;id&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &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;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;       &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;userConsumer&lt;span style=&#34;color:#1f2328&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;getUserById&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;id&lt;span style=&#34;color:#1f2328&#34;&gt;);&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;// 这是远程调用，逻辑是用户小组的服务实现的。&lt;/span&gt;&lt;span style=&#34;color:#fff&#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:#fff&#34;&gt;    &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;&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/e0c812e4fc6b43eda732c1edfba24398.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;左边的本地调用，只在应用内部调用。右边的远程调用，会跨应用在应用之间调用。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
