Playwright 是微软开源的端到端(end - to - end)测试框架,可用于现代 Web 应用。Playwright 提供如下特性:
任意浏览器、任意平台、一种 API
弹性、没有古怪的测试
无需折中、无限制:浏览器在不同进程中运行属于不同源的 Web 内容。Playwright 与现代浏览器架构保持一致,在进程外运行测试。这使得 Playwright 摆脱典型的进程内测试运行器限制。
完全隔离、快速执行:
强大的工具:
</div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">mkdir</span> playwright-demo</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">cd</span> playwright-demo/</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">python3 <span class="cm-attribute">-m</span> venv venv</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 安装 Pytest 插件</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">venv/bin/pip3 install pytest-playwright</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># 安装需要的浏览器</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">venv/bin/playwright install</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 154px;"/><div class="CodeMirror-gutters" style="display: none; height: 154px;"/></div></div></pre><h3 id="32-添加样例测试"><span>3.2. 添加样例测试</span></h3><p><span>在当前工作目录或子目录内部,创建 </span><code>test_my_application.py</code><span> 文件,其内容如下:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">re</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Page</span>, <span class="cm-variable">expect</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page</span>(<span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://playwright.dev/"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Expect a title "to contain" a substring.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">page</span>).<span class="cm-property">to_have_title</span>(<span class="cm-variable">re</span>.<span class="cm-property">compile</span>(<span class="cm-string">"Playwright"</span>))</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># create a locator</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">get_started</span> <span class="cm-operator">=</span> <span class="cm-variable">page</span>.<span class="cm-property">locator</span>(<span class="cm-string">"text=Get started"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Expect an attribute "to be strictly equal" to the value.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">get_started</span>).<span class="cm-property">to_have_attribute</span>(<span class="cm-string">"href"</span>, <span class="cm-string">"/docs/intro"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Click the get started link.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">get_started</span>.<span class="cm-property">click</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Expects the URL to contain intro.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">page</span>).<span class="cm-property">to_have_url</span>(<span class="cm-variable">re</span>.<span class="cm-property">compile</span>(<span class="cm-string">".*intro"</span>))</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 506px;"/><div class="CodeMirror-gutters" style="display: none; height: 506px;"/></div></div></pre><h3 id="33-运行样例测试"><span>3.3. 运行样例测试</span></h3><p><span>在默认情况下,测试运行在 Chromium 上,可通过 CLI 选项进行配置,测试以 Headless 模式运行。测试结果和测试日志被展示在终端中。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">venv/bin/pytest</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><hr/><h1 id="4-编写测试"><span>4. 编写测试</span></h1><p><span>Playwright 断言(assertion)是专门为动态网页创建的。检查将自动重试,直到满足必要的条件。Playwright 自带 </span><a href="https://playwright.dev/python/docs/actionability"><span>auto-wait</span></a><span>,这意味着它在执行操作前,等待到元素变为可操作的(actionable)。Playwright 提供 </span><a href="https://playwright.dev/python/docs/test-assertions"><span>expect</span></a><span> 函数来编写断言。</span></p><p><span>下面是展示如何编写使用断言、定位器(locator)和选择器(selector)的测试的示例。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">re</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Page</span>, <span class="cm-variable">expect</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_homepage_has_Playwright_in_title_and_get_started_link_linking_to_the_intro_page</span>(<span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://playwright.dev/"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Expect a title "to contain" a substring.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">page</span>).<span class="cm-property">to_have_title</span>(<span class="cm-variable">re</span>.<span class="cm-property">compile</span>(<span class="cm-string">"Playwright"</span>))</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># create a locator</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">get_started</span> <span class="cm-operator">=</span> <span class="cm-variable">page</span>.<span class="cm-property">locator</span>(<span class="cm-string">"text=Get started"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Expect an attribute "to be strictly equal" to the value.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">get_started</span>).<span class="cm-property">to_have_attribute</span>(<span class="cm-string">"href"</span>, <span class="cm-string">"/docs/intro"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Click the get started link.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">get_started</span>.<span class="cm-property">click</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Expects the URL to contain intro.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">page</span>).<span class="cm-property">to_have_url</span>(<span class="cm-variable">re</span>.<span class="cm-property">compile</span>(<span class="cm-string">".*intro"</span>))</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 506px;"/><div class="CodeMirror-gutters" style="display: none; height: 506px;"/></div></div></pre><h3 id="41-断言"><span>4.1. 断言</span></h3><p><span>Playwright 提供 </span><a href="https://playwright.dev/python/docs/test-assertions"><code>expect</code></a><span> 函数,它将一直等待,直到满足预期条件。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">re</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">expect</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">expect</span>(<span class="cm-variable">page</span>).<span class="cm-property">to_have_title</span>(<span class="cm-variable">re</span>.<span class="cm-property">compile</span>(<span class="cm-string">"Playwright"</span>))</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 88px;"/><div class="CodeMirror-gutters" style="display: none; height: 88px;"/></div></div></pre><h3 id="42-定位器"><span>4.2. 定位器</span></h3><p><span>定位器(</span><a href="https://playwright.dev/python/docs/locators"><span>Locator</span></a><span>)是 Playwright 的自动等待和重试能力的核心部分。定位器是一种随时在网页上查找元素的方法,用于在元素上执行诸如 </span><code>.click</code><span>、</span><code>.fill</code><span> 之类的操作。可以使用 </span><a href="https://playwright.dev/python/docs/api/class-page#page-locator"><span>page.locator(selector, </span><span>*</span><span>*</span><span>kwargs)</span></a><span> 方法创建自定义定位器。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">expect</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">get_started</span> <span class="cm-operator">=</span> <span class="cm-variable">page</span>.<span class="cm-property">locator</span>(<span class="cm-string">"text=Get started"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">expect</span>(<span class="cm-variable">get_started</span>).<span class="cm-property">to_have_attribute</span>(<span class="cm-string">"href"</span>, <span class="cm-string">"/docs/installation"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">get_started</span>.<span class="cm-property">click</span>()</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 154px;"/><div class="CodeMirror-gutters" style="display: none; height: 154px;"/></div></div></pre><p><span>选择器(</span><a href="https://playwright.dev/python/docs/selectors"><span>Selector</span></a><span>)是用于创建定位器的字符串。Playwright 支持许多不同的选择器,比如 </span><a href="https://playwright.dev/python/docs/selectors#text-selector"><span>Text</span></a><span>、</span><a href="https://playwright.dev/python/docs/selectors#css-selector"><span>CSS</span></a><span>、</span><a href="https://playwright.dev/python/docs/selectors#xpath-selectors"><span>XPath</span></a><span> 等。阅读 </span><a href="https://playwright.dev/python/docs/selectors"><span>in-depth guide</span></a><span> 文档,了解更多关于可用的选择器以及如何进行选择的信息。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">expect</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">expect</span>(<span class="cm-variable">page</span>.<span class="cm-property">locator</span>(<span class="cm-string">"text=Installation"</span>)).<span class="cm-property">to_be_visible</span>()</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 66px;"/><div class="CodeMirror-gutters" style="display: none; height: 66px;"/></div></div></pre><h3 id="43-测试隔离"><span>4.3. 测试隔离</span></h3><p><span>Playwright Pytest 插件基于 test fixture(比如 </span><a href="https://playwright.dev/python/docs/test-runners"><span>built in page fixture</span></a><span>)的概念,它将被传给你的测试。由于浏览器上下文,在测试之间,页面(Page)彼此隔离,这相当于开启新的浏览器行为,每个测试获得新环境,即使在一个浏览器中运行多个测试时,也是如此。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Page</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_basic_test</span>(<span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># ...</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 88px;"/><div class="CodeMirror-gutters" style="display: none; height: 88px;"/></div></div></pre><h3 id="44-使用测试钩子"><span>4.4. 使用测试钩子</span></h3><p><span>可以使用各种各样的 </span><a href="https://docs.pytest.org/en/6.2.x/fixture.html#autouse-fixtures-fixtures-you-don-t-have-to-request"><span>fixture</span></a><span> 在测试之前或之后执行代码,以及在它们之间共享对象。函数(</span><code>function</code><span>)作用域的 fixture 具有 beforeEach/afterEach 一样的自动使用行为。模块(</span><code>module</code><span>)作用域的 fixture 具有 beforeAll/afterAll 一样的自动使用行为,它在所有测试之前和所有测试之后运行。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Page</span>, <span class="cm-variable">expect</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"function"</span>, <span class="cm-variable">autouse</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">before_each_after_each</span>(<span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-builtin">print</span>(<span class="cm-string">"beforeEach"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Go to the starting url before each test.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://playwright.dev/"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">yield</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-builtin">print</span>(<span class="cm-string">"afterEach"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_main_navigation</span>(<span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Assertions use the expect API.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">expect</span>(<span class="cm-variable">page</span>).<span class="cm-property">to_have_url</span>(<span class="cm-string">"https://playwright.dev/"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 330px;"/><div class="CodeMirror-gutters" style="display: none; height: 330px;"/></div></div></pre><hr/><h1 id="5-运行测试"><span>5. 运行测试</span></h1><p><span>可以运行单个测试、一组测试或全部测试。测试可以运行在一种或多种浏览器上。默认情况下,以 Headless 方式运行测试,这意味着在运行测试时,不会打开浏览器窗口,可以在终端中看到结果。通过使用 </span><code>--headed</code><span> 标记,可以以 headed 模式运行测试。</span></p><ul><li><p><span>在 Chromium 上运行测试</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>运行单个测试文件</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest test_login.py</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>运行一组测试文件</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest tests/todo-page/ tests/landing-page/</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>使用函数名运行测试</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">-k</span> <span class="cm-string">"test_add_a_todo_item"</span></span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>以有头(headed)模式运行测试</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">--headed</span> test_login.py</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>在指定的浏览器上运行测试</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest test_login.py <span class="cm-attribute">--browser</span> webkit</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>在多种浏览器上运行测试</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest test_login.py <span class="cm-attribute">--browser</span> webkit <span class="cm-attribute">--browser</span> firefox</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre></li><li><p><span>并行运行测试</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">--numprocesses</span> auto</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><p><span>(假定已安装 </span><code>pytest-xdist</code><span>,查看 </span><a href="https://playwright.dev/python/docs/test-runners#parallelism-running-multiple-tests-at-once"><span>here</span></a><span> 获取更多信息。)</span></p></li></ul><h3 id="51-运行测试"><span>5.1. 运行测试</span></h3><p><span>因为 Playwright 运行在 Python 中,所以可以使用 Debugger 调试它。Playwright 自带 Playwright Inspector,它允许你逐步通过 Playwright API 调用,查看它们的调试日志,以及探索选择器(</span><a href="https://playwright.dev/python/docs/selectors"><span>selectors</span></a><span>)。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-def">PWDEBUG</span><span class="cm-operator">=</span><span class="cm-number">1</span> pytest <span class="cm-attribute">-s</span></span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><p><img src="https://user-images.githubusercontent.com/883973/108614092-8c478a80-73ac-11eb-9597-67dfce110e00.png" alt="108614092-8c478a80-73ac-11eb-9597-67dfce110e00.png" style="zoom:50%;"/></p><p><span>查看调试指南(</span><a href="https://playwright.dev/python/docs/debug"><span>debugging guide</span></a><span>)了解关于 </span><a href="https://playwright.dev/python/docs/debug#playwright-inspector"><span>Playwright Inspector</span></a><span> 以及使用浏览器开发者工具(</span><a href="https://playwright.dev/python/docs/debug#browser-developer-tools"><span>Browser Developer tools</span></a><span>)进行调试的更多信息。</span></p><hr/><h1 id="6-测试生成器"><span>6. 测试生成器</span></h1><p><span>Playwright 具有开箱即用的生成测试的能力,这是快速开始测试的好方法。它会打开两个窗口,一个是浏览器窗口,通过它你可以与希望测试的网站进行交互,另一个是 Playwright Inspector 窗口,通过它你可以录制测试、拷贝测试、清除测试以及改变测试的语言。</span></p><p><strong><span>你将学习:</span></strong></p><ul><li><a href="https://playwright.dev/python/docs/codegen#running-codegen"><span>How to generate tests with Codegen</span></a></li></ul><h3 id="61-运行-codegen"><span>6.1. 运行 Codegen</span></h3><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">playwright codegen playwright.dev</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><p><span>运行 </span><code>codegen</code><span>,然后在浏览器中执行操作。Playwright 将为用户的交互生成代码。</span><code>Codegen</code><span> 尝试生成弹性的基于文本的选择器。</span></p><p><img src="https://user-images.githubusercontent.com/13063165/181852815-971c10da-0b55-4e54-8a73-77e1e825193c.png" alt="181852815-971c10da-0b55-4e54-8a73-77e1e825193c.png" style="zoom:50%;"/></p><p><span>当你完成与页面的交互时,按下 </span><strong><span>record</span></strong><span> 按钮停止录制,使用 </span><strong><span>copy</span></strong><span> 按钮把生成的代码拷贝到编辑器。</span></p><p><img src="https://user-images.githubusercontent.com/13063165/183905981-003c4173-0d5e-4960-8190-50e6ca71b2c3.png" alt="183905981-003c4173-0d5e-4960-8190-50e6ca71b2c3.png" style="zoom:50%;"/></p><p><span>使用 </span><strong><span>clear</span></strong><span> 按钮清除代码,重新开始录制。完成时,关闭 Playwright Inspector 窗口,或停止终端命令。</span></p><p><span>如欲了解有关生成测试的更多信息,请查看 </span><a href="https://playwright.dev/python/docs/codegen"><span>Codegen</span></a><span> 的详细指南。</span></p><hr/><h1 id="7-追踪查看器trace-viewer)"><span>7. 追踪查看器(Trace Viewer)</span></h1><p><span>Playwright 追踪查看器是一个 GUI 工具,它使你可以探查测试中记录的 Playwright 追踪,可以在测试的每个操作中来回移动,可视化地查看每个操作期间正在发生什么。</span></p><p><span>你将学习:</span></p><ul><li><span>如何记录追踪</span></li><li><span>如何打开 HTML 报告</span></li><li><span>如何打开追踪查看器</span></li></ul><h3 id="71-记录追踪"><span>7.1. 记录追踪</span></h3><p><span>像下面一样使用 </span><a href="https://playwright.dev/python/docs/api/class-browsercontext#browser-context-tracing"><span>browser_context.tracing</span></a><span> API 记录追踪:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">browser</span> <span class="cm-operator">=</span> <span class="cm-variable">chromium</span>.<span class="cm-property">launch</span>()</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">context</span> <span class="cm-operator">=</span> <span class="cm-variable">browser</span>.<span class="cm-property">new_context</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Start tracing before creating / navigating a page.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">context</span>.<span class="cm-property">tracing</span>.<span class="cm-property">start</span>(<span class="cm-variable">screenshots</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>, <span class="cm-variable">snapshots</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>, <span class="cm-variable">sources</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://playwright.dev"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Stop tracing and export it into a zip archive.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">context</span>.<span class="cm-property">tracing</span>.<span class="cm-property">stop</span>(<span class="cm-variable">path</span> <span class="cm-operator">=</span> <span class="cm-string">"trace.zip"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 242px;"/><div class="CodeMirror-gutters" style="display: none; height: 242px;"/></div></div></pre><p><span>这将记录追踪,把它导出到名称为 </span><code>trace.zip</code><span> 的文件中。</span></p><h3 id="72-打开追踪"><span>7.2. 打开追踪</span></h3><p><span>可以使用 Playwright CLI 打开保存的追踪。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">playwright show-trace trace.zip</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><h3 id="73-查看追踪"><span>7.3. 查看追踪</span></h3><p><span>通过单击每个操作或使用时间轴悬停,查看测试的追踪,以及查看操作前后的页面状态。在测试的每个步骤期间查看日志、源和网络。追踪查看器创建 DOM 快照,因此你可以与它进行交互,打开开发者工具(devtools)等。</span></p><p><img src="https://user-images.githubusercontent.com/13063165/189136442-4fc6d7a3-6f0c-4a5f-9d36-2650018b018a.png" alt="189136442-4fc6d7a3-6f0c-4a5f-9d36-2650018b018a.png" style="zoom:50%;"/></p><p><span>如欲了解更多信息,请查看 </span><a href="https://playwright.dev/python/docs/trace-viewer"><span>Trace Viewer</span></a><span> 的详细指南。</span></p><hr/><h1 id="8-pytest-插件参考"><span>8. Pytest 插件参考</span></h1><p><span>Playwright 提供 </span><a href="https://docs.pytest.org/en/stable/"><span>Pytest</span></a><span> 插件,来编写端到端的测试。如果想开始使用它,请参考 </span><a href="https://playwright.dev/python/docs/intro"><span>getting started guide</span></a><span>。</span></p><h3 id="81-用法"><span>8.1. 用法</span></h3><p><span>使用 </span><a href="https://docs.pytest.org/en/stable/"><span>Pytest</span></a><span> CLI 运行测试:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">--browser</span> webkit <span class="cm-attribute">--headed</span></span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><p><span>如果你想自动地添加 CLI 参数,而不是指定它们,请使用 </span><a href="https://docs.pytest.org/en/stable/reference.html#ini-options-ref"><span>pytest.ini</span></a><span> 文件。</span></p><h3 id="82-cli-参数"><span>8.2. CLI 参数</span></h3><ul><li><code>--headed</code><span>:以有头模式运行测试(默认:无头)</span></li><li><code>--browser</code><span>:用不同的浏览器 </span><code>chromium</code><span>、</span><code>firefox</code><span>、</span><code>webkit</code><span> 运行测试。可以指定多次(默认:所有浏览器)</span></li><li><code>--browser-channel</code><span>:使用的 </span><a href="https://playwright.dev/python/docs/browsers"><span>Browser channel</span></a></li><li><code>--slow-mo</code><span>:使用慢动作运行测试</span></li><li><code>--device</code><span>:要模拟的设备(</span><a href="https://playwright.dev/python/docs/emulation"><span>Device</span></a><span>)</span></li><li><code>--output</code><span>:用于测试生成的制品(aritifact)的目录(默认:</span><code>test-results</code><span>)</span></li><li><code>--tracing</code><span>:是否为每次测试记录追踪(</span><a href="https://playwright.dev/python/docs/trace-viewer"><span>trace</span></a><span>)。</span><code>on</code><span>、</span><code>off</code><span> 或 </span><code>retain-on-failure</code><span>(默认:</span><code>off</code><span>)</span></li><li><code>--video</code><span>:是否为每次测试录制视频。</span><code>on</code><span>、</span><code>off</code><span> 或 </span><code>retain-on-failure</code><span>(默认:</span><code>off</code><span>)</span></li><li><code>--screenshot</code><span>:是否在每次测试后,自动地捕获截图。</span><code>on</code><span>, </span><code>off</code><span>, or </span><code>only-on-failure</code><span> (默认:</span><code>off</code><span>)</span></li></ul><h3 id="83-fixture"><span>8.3. Fixture</span></h3><p><span>该插件为 pytest 配置 Playwright 特定的 fixture(</span><a href="https://docs.pytest.org/en/latest/fixture.html"><span>fixtures for pytest</span></a><span>)。为使用这些 fixture,使用 fixture 名称作为测试函数的参数。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_my_app_is_working</span>(<span class="cm-variable">fixture_name</span>):</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Test using fixture_name</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># ...</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 66px;"/><div class="CodeMirror-gutters" style="display: none; height: 66px;"/></div></div></pre><p><strong><span>函数作用域</span></strong><span>:这些 fixture 在测试函数请求时创建,在测试结束时销毁。</span></p><ul><li><code>context</code><span>:用于测试的新浏览器上下文(</span><a href="https://playwright.dev/python/docs/browser-contexts"><span>browser context</span></a><span>)</span></li><li><code>page</code><span>:用于测试的新浏览器页面(</span><a href="https://playwright.dev/python/docs/pages"><span>browser page</span></a><span>)</span></li></ul><p><strong><span>会话作用域</span></strong><span>:这些 fixture 在测试函数请求时创建,在测试结束时销毁。</span></p><ul><li><code>playwright</code><span>:</span><a href="https://playwright.dev/python/docs/api/class-playwright"><span>Playwright</span></a><span> 实例</span></li><li><code>browser_type</code><span>:当前浏览器的 </span><a href="https://playwright.dev/python/docs/api/class-browsertype"><span>BrowserType</span></a><span> 实例</span></li><li><code>browser</code><span>:Playwright 启动的 </span><a href="https://playwright.dev/python/docs/api/class-browser"><span>Browser</span></a><span> 实例</span></li><li><code>browser_name</code><span>:浏览器名称</span></li><li><code>browser_channel</code><span>:浏览器通道(channel)</span></li><li><code>is_chromium</code><span>、</span><code>is_webkit</code><span>、</span><code>is_firefox</code><span>:各自浏览器类型的布尔值</span></li></ul><p><strong><span>自定义 fixture 选项</span></strong><span>:对于 </span><code>browser</code><span> 和 </span><code>context</code><span> fixture,使用下面的 fixture 来定义自定义启动选项。</span></p><ul><li><code>browser_type_launch_args</code><span>:重写用于 </span><a href="https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch"><span>browser_type.launch(**kwargs)</span></a><span> 的启动参数。它应该返回字典</span></li><li><code>browser_context_args</code><span>:重写用于 </span><a href="https://playwright.dev/python/docs/api/class-browser#browser-new-context"><span>browser.new_context(**kwargs)</span></a><span> 的选项。它应该返回字典</span></li></ul><h3 id="84-并行同时运行多个测试"><span>8.4. 并行:同时运行多个测试</span></h3><p><span>如果测试运行在有多个 CPU 的机器上,可以通过使用 </span><a href="https://pypi.org/project/pytest-xdist/"><code>pytest-xdist</code></a><span> 同时运行多个测试,加快测试套件的整体执行时间。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># install dependency</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pip</span> <span class="cm-variable">install</span> <span class="cm-variable">pytest</span><span class="cm-operator">-</span><span class="cm-variable">xdist</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># use the --numprocesses flag</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">pytest</span> <span class="cm-operator">--</span><span class="cm-variable">numprocesses</span> <span class="cm-variable">auto</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 88px;"/><div class="CodeMirror-gutters" style="display: none; height: 88px;"/></div></div></pre><p><span>根据硬件和测试的特性,可以将 </span><code>numprocesses</code><span> 设置为 2 到机器上 CPU 数量之间的任意值。如果设置得过高,可能产生非预期行为。</span></p><p><span>有关 </span><code>pytest</code><span> 选项的常用信息,请参考 </span><a href="https://playwright.dev/python/docs/running-tests"><span>Running Tests</span></a><span>。</span></p><h3 id="85-示例"><span>8.5. 示例</span></h3><p><strong><span>配置 Mypy 类型以自动补全</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># test_my_application.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Page</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_visit_admin_dashboard</span>(<span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"/admin"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># ...</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 132px;"/><div class="CodeMirror-gutters" style="display: none; height: 132px;"/></div></div></pre><p><strong><span>配置慢动作</span></strong></p><p><span>使用 </span><code>--slowmo</code><span> 参数以慢动作运行测试。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">--slowmo</span> <span class="cm-number">100</span></span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><p><strong><span>通过浏览器跳过测试</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># test_my_application.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">mark</span>.<span class="cm-property">skip_browser</span>(<span class="cm-string">"firefox"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_visit_example</span>(<span class="cm-variable">page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://example.com"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># ...</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 154px;"/><div class="CodeMirror-gutters" style="display: none; height: 154px;"/></div></div></pre><p><strong><span>在特定的浏览器上运行测试</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># conftest.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">mark</span>.<span class="cm-property">only_browser</span>(<span class="cm-string">"chromium"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_visit_example</span>(<span class="cm-variable">page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://example.com"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># ...</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 154px;"/><div class="CodeMirror-gutters" style="display: none; height: 154px;"/></div></div></pre><p><strong><span>使用自定义的浏览器通道运行</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">--browser-channel</span> chrome</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># test_my_application.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_example</span>(<span class="cm-variable">page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://example.com"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 66px;"/><div class="CodeMirror-gutters" style="display: none; height: 66px;"/></div></div></pre><p><strong><span>配置 base</span><span>-</span><span>url</span></strong></p><p><span>使用 </span><code>base-url</code><span> 参数启动 Pytest。</span><a href="https://github.com/pytest-dev/pytest-base-url"><code>pytest-base-url</code></a><span> 插件允许通过配置、CLI 参数或像 fixture 一样设置 base url。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">pytest <span class="cm-attribute">--base-url</span> http://localhost:8080</span></pre></div></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 22px;"/><div class="CodeMirror-gutters" style="display: none; height: 22px;"/></div></div></pre><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># test_my_application.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_visit_example</span>(<span class="cm-variable">page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"/admin"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># -> Will result in http://localhost:8080/admin</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 88px;"/><div class="CodeMirror-gutters" style="display: none; height: 88px;"/></div></div></pre><p><strong><span>忽略 HTTPS 错误</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># conftest.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">browser_context_args</span>(<span class="cm-variable">browser_context_args</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">return</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-operator">**</span><span class="cm-variable">browser_context_args</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"ignore_https_errors"</span>: <span class="cm-keyword">True</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 198px;"/><div class="CodeMirror-gutters" style="display: none; height: 198px;"/></div></div></pre><p><strong><span>使用自定义窗口大小</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># conftest.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">browser_context_args</span>(<span class="cm-variable">browser_context_args</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">return</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-operator">**</span><span class="cm-variable">browser_context_args</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"viewport"</span>: {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"width"</span>: <span class="cm-number">1920</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"height"</span>: <span class="cm-number">1080</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 264px;"/><div class="CodeMirror-gutters" style="display: none; height: 264px;"/></div></div></pre><p><strong><span>设备仿真</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="shell"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="shell"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># conftest.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">import pytest</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">@pytest.fixture<span class="cm-def">(scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">def browser_context_args(browser_context_args, playwright):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> iphone_11 <span class="cm-operator">=</span> playwright.devices[<span class="cm-string">'iPhone 11 Pro'</span>]</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> return {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> **browser_context_args,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> **iphone_11,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 220px;"/><div class="CodeMirror-gutters" style="display: none; height: 220px;"/></div></div></pre><p><span>或通过命令行 </span><code>--device="iPhone 11 Pro"</code><span>。</span></p><p><strong><span>持久化上下文</span></strong></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># conftest.py</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">BrowserType</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">typing</span> <span class="cm-keyword">import</span> <span class="cm-variable">Dict</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">context</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">browser_type</span>: <span class="cm-variable">BrowserType</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">browser_type_launch_args</span>: <span class="cm-variable">Dict</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">browser_context_args</span>: <span class="cm-variable">Dict</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">context</span> <span class="cm-operator">=</span> <span class="cm-variable">browser_type</span>.<span class="cm-property">launch_persistent_context</span>(<span class="cm-string">"./foobar"</span>, <span class="cm-operator">**</span>{</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-operator">**</span><span class="cm-variable">browser_type_launch_args</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-operator">**</span><span class="cm-variable">browser_context_args</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"locale"</span>: <span class="cm-string">"de-DE"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> })</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">yield</span> <span class="cm-variable">context</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">context</span>.<span class="cm-property">close</span>()</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 418px;"/><div class="CodeMirror-gutters" style="display: none; height: 418px;"/></div></div></pre><p><span>从持久化上下文创建测试内部的所有页面。</span></p><p><strong><span>与 </span><code>unittest.TestCase</code><span> 一起使用</span></strong></p><p><span>参考下面的示例,了解如何与 </span><code>unittest.TestCase</code><span> 一起使用。这有一个限制,仅能指定一个浏览器,并且在指定多个浏览器时,不会生成多个浏览器的矩阵。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">unittest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Page</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">class</span> <span class="cm-def">MyTest</span>(<span class="cm-variable">unittest</span>.<span class="cm-property">TestCase</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">autouse</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">def</span> <span class="cm-def">setup</span>(<span class="cm-variable-2">self</span>, <span class="cm-variable">page</span>: <span class="cm-variable">Page</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable-2">self</span>.<span class="cm-property">page</span> <span class="cm-operator">=</span> <span class="cm-variable">page</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">def</span> <span class="cm-def">test_foobar</span>(<span class="cm-variable-2">self</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable-2">self</span>.<span class="cm-property">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://microsoft.com"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable-2">self</span>.<span class="cm-property">page</span>.<span class="cm-property">locator</span>(<span class="cm-string">"#foobar"</span>).<span class="cm-property">click</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable-2">self</span>.<span class="cm-property">page</span>.<span class="cm-property">evaluate</span>(<span class="cm-string">"1 + 1"</span>) <span class="cm-operator">==</span> <span class="cm-number">2</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 330px;"/><div class="CodeMirror-gutters" style="display: none; height: 330px;"/></div></div></pre><h3 id="86-调试"><span>8.6. 调试</span></h3><h5 id="861-使用-pdb"><span>8.6.1 使用 pdb</span></h5><p><span>在代码中使用 </span><code>breakpoint()</code><span> 语句停止执行,获取 </span><a href="https://docs.python.org/3/library/pdb.html"><span>pdb</span></a><span> REPL。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_bing_is_working</span>(<span class="cm-variable">page</span>):</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://bing.com"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">breakpoint</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># ...</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 88px;"/><div class="CodeMirror-gutters" style="display: none; height: 88px;"/></div></div></pre><h3 id="87-部署到-ci"><span>8.7. 部署到 CI</span></h3><p><span>请查看 </span><a href="https://playwright.dev/python/docs/ci"><span>guides for CI providers</span></a><span> 获取关于将测试部署到 CI/CD 的信息。</span></p><hr/><h1 id="9-认证"><span>9. 认证</span></h1><p><span>Playwright 可用于需要认证的自动化场景。</span></p><p><span>使用 Playwright 编写的测试在被称为浏览器上下文(</span><a href="https://playwright.dev/python/docs/browser-contexts"><span>browser contexts</span></a><span>)的、独立的、干净的环境中执行。这种隔离模型可以提升复现性,防止级联测试失败。新浏览器上下文可以加载现有的认证状态。这可以消除在每个上下文中登录的需求,加快测试执行的速度。</span></p><blockquote><p><span>注意:本指南覆盖 cookie/token</span><span>-</span><span>based 认证(通过 app UI 登陆)。对于 HTTP 认证(</span><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication"><span>HTTP authentication</span></a><span>),请使用 </span><a href="https://playwright.dev/python/docs/api/class-browser#browser-new-context"><span>browser.new_context(**kwargs)</span></a><span>。</span></p></blockquote><h3 id="91-自动化登录"><span>9.1. 自动化登录</span></h3><p><span>Playwright API 可以与登陆表单自动化交互(</span><a href="https://playwright.dev/python/docs/input"><span>automate interaction</span></a><span>)。</span></p><p><span>下面的示例自动化登陆到 Github。执行这些步骤后,浏览器上下文将被认证。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span> <span class="cm-operator">=</span> <span class="cm-variable">context</span>.<span class="cm-property">new_page</span>()</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">'https://github.com/login'</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Interact with login form</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">get_by_text</span>(<span class="cm-string">"Login"</span>).<span class="cm-property">click</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">get_by_label</span>(<span class="cm-string">"User Name"</span>).<span class="cm-property">fill</span>(<span class="cm-variable">USERNAME</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">get_by_label</span>(<span class="cm-string">"Password"</span>).<span class="cm-property">fill</span>(<span class="cm-variable">PASSWORD</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">get_by_text</span>(<span class="cm-string">'Submit'</span>).<span class="cm-property">click</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Continue with the test</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 198px;"/><div class="CodeMirror-gutters" style="display: none; height: 198px;"/></div></div></pre><p><span>为每次测试重做登录将减慢测试的执行速度。为缓解这种情况,应该重用现有的认证状态。</span></p><h3 id="92-重用签入状态"><span>9.2. 重用签入状态</span></h3><p><span>Playwright 提供在测试中重用签入(signed</span><span>-</span><span>in)状态的方式。通过该方式,可以只登陆一次,然后跳过所有测试的登陆步骤。</span></p><p><span>Web 应用使用基于 Cookie 或基于 Token 的认证,认证状态被当作 Cookie 存储,或被存储在 Local Storage 中。Playwright 提供 </span><a href="https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state"><span>browserContext.storageState(options)</span></a><span> 方法,可使用它从已认证上下文中获取存储状态,然后使用预填充状态创建新上下文。</span></p><p><span>Cookie 和 Local Storage 状态可以跨不同的浏览器使用。它们依赖应用程序的认证模型:有些应用程序可能同时需要 Cookie 和 Local Storage。</span></p><p><span>下面的代码片段从已认证上下文中获取状态,然后使用该状态创建新上下文。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Save storage state into the file.</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">storage</span> <span class="cm-operator">=</span> <span class="cm-variable">context</span>.<span class="cm-property">storage_state</span>(<span class="cm-variable">path</span><span class="cm-operator">=</span><span class="cm-string">"state.json"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Create a new context with the saved storage state.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">context</span> <span class="cm-operator">=</span> <span class="cm-variable">browser</span>.<span class="cm-property">new_context</span>(<span class="cm-variable">storage_state</span><span class="cm-operator">=</span><span class="cm-string">"state.json"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 110px;"/><div class="CodeMirror-gutters" style="display: none; height: 110px;"/></div></div></pre><h3 id="93-session-storage"><span>9.3. Session Storage</span></h3><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage"><span>Session Storage</span></a><span> 很少用于存储与登陆状态相关的信息。Session Storage 特定于特定的域,页面加载时它不会持久化。Playwright 未提供持久化 Session Storage 的 API,但下面的片段可用于保存/加载 Session Storage。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">os</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Get session storage and store as env variable</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">session_storage</span> <span class="cm-operator">=</span> <span class="cm-variable">page</span>.<span class="cm-property">evaluate</span>(<span class="cm-string">"() => JSON.stringify(sessionStorage)"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">os</span>.<span class="cm-property">environ</span>[<span class="cm-string">"SESSION_STORAGE"</span>] <span class="cm-operator">=</span> <span class="cm-variable">session_storage</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># Set session storage in a new context</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">session_storage</span> <span class="cm-operator">=</span> <span class="cm-variable">os</span>.<span class="cm-property">environ</span>[<span class="cm-string">"SESSION_STORAGE"</span>]</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">context</span>.<span class="cm-property">add_init_script</span>(<span class="cm-string">"""(storage => {</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string"> if (window.location.hostname === 'example.com') {</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string"> const entries = JSON.parse(storage)</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string"> for (const [key, value] of Object.entries(entries)) {</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string"> window.sessionStorage.setItem(key, value)</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string"> }</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string"> }</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-string">})('"""</span> <span class="cm-operator">+</span> <span class="cm-variable">session_storage</span> <span class="cm-operator">+</span> <span class="cm-string">"')"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 352px;"/><div class="CodeMirror-gutters" style="display: none; height: 352px;"/></div></div></pre><h3 id="94-多因子认证"><span>9.4. 多因子认证</span></h3><p><span>使用多因子认证(MFA)的账户无法完全自动化,需要人工干预。持久化认证可用于部分自动化 MFA 场景。</span></p><h5 id="941-持久化认证"><span>9.4.1 持久化认证</span></h5><p><span>注意:持久化认证不适用于 CI 环境,因为它依赖磁盘位置。用户数据目录特定于浏览器类型,不能跨浏览器类型共享。</span></p><p><span>用户数据目录可以与 </span><a href="https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch-persistent-context"><span>browser_type.launch_persistent_context(user_data_dir, **kwargs)</span></a><span> API 一起使用。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">sync_playwright</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">with</span> <span class="cm-variable">sync_playwright</span>() <span class="cm-keyword">as</span> <span class="cm-variable">p</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">user_data_dir</span> <span class="cm-operator">=</span> <span class="cm-string">'/path/to/directory'</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">browser</span> <span class="cm-operator">=</span> <span class="cm-variable">p</span>.<span class="cm-property">chromium</span>.<span class="cm-property">launch_persistent_context</span>(<span class="cm-variable">user_data_dir</span>, <span class="cm-variable">headless</span><span class="cm-operator">=</span><span class="cm-keyword">False</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Execute login steps manually in the browser window</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 154px;"/><div class="CodeMirror-gutters" style="display: none; height: 154px;"/></div></div></pre><p><strong><span>生命周期</span></strong></p><ol start=""><li><span>在磁盘上创建用户数据目录</span></li><li><span>使用用户数据目录启动持久化上下文,然后登陆 MFA 账户</span></li><li><span>重用用户数据目录来运行自动化场景</span></li></ol><hr/><h1 id="10-事件"><span>10. 事件</span></h1><p><span>Playwright 允许监听发生在 Web 页面上的多种类型的事件,比如网络请求、子页面的创建、专用 Worker 等。可通过多种方式订阅这些事件,比如等待事件或添加/移除事件监听者。</span></p><h3 id="101-等待事件"><span>10.1. 等待事件</span></h3><p><span>大多数时间,脚本需要等待特定的事件发生。下面是一些典型的事件等待模式。</span></p><p><span>使用 </span><a href="https://playwright.dev/python/docs/api/class-page#page-wait-for-request"><span>page.expect_request(url_or_predicate, **kwargs)</span></a><span> 等待拥有指定 URL 的请求:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">with</span> <span class="cm-variable">page</span>.<span class="cm-property">expect_request</span>(<span class="cm-string">"**/*logo*.png"</span>) <span class="cm-keyword">as</span> <span class="cm-variable">first</span>:</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://wikipedia.org"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-builtin">print</span>(<span class="cm-variable">first</span>.<span class="cm-property">value</span>.<span class="cm-property">url</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 66px;"/><div class="CodeMirror-gutters" style="display: none; height: 66px;"/></div></div></pre><p><span>等待弹出窗口:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">with</span> <span class="cm-variable">page</span>.<span class="cm-property">expect_popup</span>() <span class="cm-keyword">as</span> <span class="cm-variable">popup</span>:</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">page</span>.<span class="cm-property">evaluate</span>(<span class="cm-string">"window.open()"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">popup</span>.<span class="cm-property">value</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://wikipedia.org"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 66px;"/><div class="CodeMirror-gutters" style="display: none; height: 66px;"/></div></div></pre><h3 id="102-添加移除事件监听者"><span>10.2. 添加/移除事件监听者</span></h3><p><span>有时,事件发生在随机时间,而不是等待它们,它们需要被处理。Playwright 支持订阅/取消订阅事件的传统语言机制:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">print_request_sent</span>(<span class="cm-variable">request</span>):</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-builtin">print</span>(<span class="cm-string">"Request sent: "</span> <span class="cm-operator">+</span> <span class="cm-variable">request</span>.<span class="cm-property">url</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">print_request_finished</span>(<span class="cm-variable">request</span>):</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-builtin">print</span>(<span class="cm-string">"Request finished: "</span> <span class="cm-operator">+</span> <span class="cm-variable">request</span>.<span class="cm-property">url</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">on</span>(<span class="cm-string">"request"</span>, <span class="cm-variable">print_request_sent</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">on</span>(<span class="cm-string">"requestfinished"</span>, <span class="cm-variable">print_request_finished</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://wikipedia.org"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">remove_listener</span>(<span class="cm-string">"requestfinished"</span>, <span class="cm-variable">print_request_finished</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">goto</span>(<span class="cm-string">"https://www.openstreetmap.org/"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 264px;"/><div class="CodeMirror-gutters" style="display: none; height: 264px;"/></div></div></pre><h3 id="103-添加一次性监听者"><span>10.3. 添加一次性监听者</span></h3><p><span>如果特定事件需要被处理一次,那么可以使用便捷的 API:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation"><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">once</span>(<span class="cm-string">"dialog"</span>, <span class="cm-keyword">lambda</span> <span class="cm-variable">dialog</span>: <span class="cm-variable">dialog</span>.<span class="cm-property">accept</span>(<span class="cm-string">"2021"</span>))</span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">page</span>.<span class="cm-property">evaluate</span>(<span class="cm-string">"prompt('Enter a number:')"</span>)</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 44px;"/><div class="CodeMirror-gutters" style="display: none; height: 44px;"/></div></div></pre><hr/><h1 id="11-api-测试"><span>11. API 测试</span></h1><p><span>Playwright 可用于访问应用程序的 REST API。</span></p><p><span>有时,你可能想通过 Python 直接向服务端发送请求,而非加载页面,在其中运行 js 代码。此时,Playwright 可以派上用场的一些示例如下:</span></p><ul><li><span>测试服务端 API</span></li><li><span>在访问 Web 应用程序之前,准备服务端状态</span></li><li><span>在浏览器中运行一些操作后,验证服务端的后置条件</span></li></ul><p><span>这些都可以通过 </span><a href="https://playwright.dev/python/docs/api/class-apirequestcontext"><span>APIRequestContext</span></a><span> 方法实现。</span></p><p><span>下面的示例依赖 </span><a href="https://playwright.dev/python/docs/test-runners"><code>pytest-playwright</code></a><span> 包,它向 Pytest test</span><span>-</span><span>runner 添加 Playwright fixture。</span></p><ul><li><a href="https://playwright.dev/python/docs/api-testing#writing-api-test"><span>Writing API Test</span></a></li><li><a href="https://playwright.dev/python/docs/api-testing#prepare-server-state-via-api-calls"><span>Prepare server state via API calls</span></a></li><li><a href="https://playwright.dev/python/docs/api-testing#check-the-server-state-after-running-user-actions"><span>Check the server state after running user actions</span></a></li><li><a href="https://playwright.dev/python/docs/api-testing#reuse-authentication-state"><span>Reuse authentication state</span></a></li></ul><h3 id="111-编写-api-测试"><span>11.1. 编写 API 测试</span></h3><p><a href="https://playwright.dev/python/docs/api/class-apirequestcontext"><span>APIRequestContext</span></a><span> 可以通过网络发送各种 HTTP(S) 请求。</span></p><p><span>下面的示例展示如何使用 Playwright 通过 </span><a href="https://docs.github.com/en/rest"><span>GitHub API</span></a><span> 测试 issue 创建。测试套件做如下事情:</span></p><ul><li><span>在运行测试之前,创建新仓库</span></li><li><span>创建一些 issue 及验证服务端状态</span></li><li><span>在运行测试后删除仓库</span></li></ul><h5 id="1111-配置"><span>11.1.1. 配置</span></h5><p><span>Github API 需要认证,因此我们将为所有测试配置一次 token。同时,我们也将设置 </span><code>baseURL</code><span> 以简化测试。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">os</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">typing</span> <span class="cm-keyword">import</span> <span class="cm-variable">Generator</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Playwright</span>, <span class="cm-variable">APIRequestContext</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_API_TOKEN</span> <span class="cm-operator">=</span> <span class="cm-variable">os</span>.<span class="cm-property">getenv</span>(<span class="cm-string">"GITHUB_API_TOKEN"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">assert</span> <span class="cm-variable">GITHUB_API_TOKEN</span>, <span class="cm-string">"GITHUB_API_TOKEN is not set"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">api_request_context</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">playwright</span>: <span class="cm-variable">Playwright</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">) <span class="cm-operator">-></span> <span class="cm-variable">Generator</span>[<span class="cm-variable">APIRequestContext</span>, <span class="cm-keyword">None</span>, <span class="cm-keyword">None</span>]:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">headers</span> <span class="cm-operator">=</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># We set this header per GitHub guidelines.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"Accept"</span>: <span class="cm-string">"application/vnd.github.v3+json"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Add authorization token to all requests.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Assuming personal access token available in the environment.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"Authorization"</span>: <span class="cm-string">f"token </span>{<span class="cm-variable">GITHUB_API_TOKEN</span>}<span class="cm-string">"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">request_context</span> <span class="cm-operator">=</span> <span class="cm-variable">playwright</span>.<span class="cm-property">request</span>.<span class="cm-property">new_context</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">base_url</span><span class="cm-operator">=</span><span class="cm-string">"https://api.github.com"</span>, <span class="cm-variable">extra_http_headers</span><span class="cm-operator">=</span><span class="cm-variable">headers</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> )</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">yield</span> <span class="cm-variable">request_context</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">request_context</span>.<span class="cm-property">dispose</span>()</span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 616px;"/><div class="CodeMirror-gutters" style="display: none; height: 616px;"/></div></div></pre><h5 id="1112-编写测试"><span>11.1.2. 编写测试</span></h5><p><span>现在我们已初始化请求对象,我们可以添加一些在仓库中创建新 issue 的测试。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">os</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">typing</span> <span class="cm-keyword">import</span> <span class="cm-variable">Generator</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Playwright</span>, <span class="cm-variable">APIRequestContext</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_API_TOKEN</span> <span class="cm-operator">=</span> <span class="cm-variable">os</span>.<span class="cm-property">getenv</span>(<span class="cm-string">"GITHUB_API_TOKEN"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">assert</span> <span class="cm-variable">GITHUB_API_TOKEN</span>, <span class="cm-string">"GITHUB_API_TOKEN is not set"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_USER</span> <span class="cm-operator">=</span> <span class="cm-variable">os</span>.<span class="cm-property">getenv</span>(<span class="cm-string">"GITHUB_USER"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">assert</span> <span class="cm-variable">GITHUB_USER</span>, <span class="cm-string">"GITHUB_USER is not set"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_REPO</span> <span class="cm-operator">=</span> <span class="cm-string">"test"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># ...</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_should_create_bug_report</span>(<span class="cm-variable">api_request_context</span>: <span class="cm-variable">APIRequestContext</span>) <span class="cm-operator">-></span> <span class="cm-keyword">None</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">data</span> <span class="cm-operator">=</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"title"</span>: <span class="cm-string">"[Bug] report 1"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"body"</span>: <span class="cm-string">"Bug description"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">new_issue</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">post</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">/issues"</span>, <span class="cm-variable">data</span><span class="cm-operator">=</span><span class="cm-variable">data</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">new_issue</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issues</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">get</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">/issues"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issues</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issues_response</span> <span class="cm-operator">=</span> <span class="cm-variable">issues</span>.<span class="cm-property">json</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issue</span> <span class="cm-operator">=</span> <span class="cm-builtin">list</span>(<span class="cm-builtin">filter</span>(<span class="cm-keyword">lambda</span> <span class="cm-variable">issue</span>: <span class="cm-variable">issue</span>[<span class="cm-string">"title"</span>] <span class="cm-operator">==</span> <span class="cm-string">"[Bug] report 1"</span>, <span class="cm-variable">issues_response</span>))[<span class="cm-number">0</span>]</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issue</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issue</span>[<span class="cm-string">"body"</span>] <span class="cm-operator">==</span> <span class="cm-string">"Bug description"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_should_create_feature_request</span>(<span class="cm-variable">api_request_context</span>: <span class="cm-variable">APIRequestContext</span>) <span class="cm-operator">-></span> <span class="cm-keyword">None</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">data</span> <span class="cm-operator">=</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"title"</span>: <span class="cm-string">"[Feature] request 1"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"body"</span>: <span class="cm-string">"Feature description"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">new_issue</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">post</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">/issues"</span>, <span class="cm-variable">data</span><span class="cm-operator">=</span><span class="cm-variable">data</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">new_issue</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issues</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">get</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">/issues"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issues</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issues_response</span> <span class="cm-operator">=</span> <span class="cm-variable">issues</span>.<span class="cm-property">json</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issue</span> <span class="cm-operator">=</span> <span class="cm-builtin">list</span>(<span class="cm-builtin">filter</span>(<span class="cm-keyword">lambda</span> <span class="cm-variable">issue</span>: <span class="cm-variable">issue</span>[<span class="cm-string">"title"</span>] <span class="cm-operator">==</span> <span class="cm-string">"[Feature] request 1"</span>, <span class="cm-variable">issues_response</span>))[<span class="cm-number">0</span>]</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issue</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issue</span>[<span class="cm-string">"body"</span>] <span class="cm-operator">==</span> <span class="cm-string">"Feature description"</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 1254px;"/><div class="CodeMirror-gutters" style="display: none; height: 1254px;"/></div></div></pre><h5 id="1113-准备setup)和销毁teardown)"><span>11.1.3. 准备(setup)和销毁(teardown)</span></h5><p><span>这些测试假定仓库存在。你可能想在运行测试之前创建新仓库,之后删除它。为此使用 </span><a href="https://docs.pytest.org/en/stable/fixture.html#fixture-scopes"><span>session fixture</span></a><span>。</span><code>yield</code><span> 之前的部分在所有测试之前执行,之后的部分在所有测试之后执行。</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-comment"># ...</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>, <span class="cm-variable">autouse</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">create_test_repository</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">api_request_context</span>: <span class="cm-variable">APIRequestContext</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">) <span class="cm-operator">-></span> <span class="cm-variable">Generator</span>[<span class="cm-keyword">None</span>, <span class="cm-keyword">None</span>, <span class="cm-keyword">None</span>]:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Before all</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">new_repo</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">post</span>(<span class="cm-string">"/user/repos"</span>, <span class="cm-variable">data</span><span class="cm-operator">=</span>{<span class="cm-string">"name"</span>: <span class="cm-variable">GITHUB_REPO</span>})</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">new_repo</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">yield</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># After all</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">deleted_repo</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">delete</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">deleted_repo</span>.<span class="cm-property">ok</span></span></pre></div></div></div></div></div><div style="position: absolute; ; width: 1px; border-bottom-width: 0px; border-bottom-style: solid; border-bottom-color: transparent; top: 308px;"/><div class="CodeMirror-gutters" style="display: none; height: 308px;"/></div></div></pre><h5 id="1114-完成样例测试"><span>11.1.4. 完成样例测试</span></h5><p><span>这是 API 测试的完整样例:</span></p><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" style="break-inside: unset;"><div class="CodeMirror cm-s-inner cm-s-null-scroll CodeMirror-wrap" lang="python"><div style="overflow: hidden; position: relative; width: 3px; ; top: 9px; left: 8px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"/></div><div class="CodeMirror-scrollbar-filler" cm-not-content="true"/><div class="CodeMirror-gutter-filler" cm-not-content="true"/><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="margin-left: 0px; margin-bottom: 0px; border-right-width: 0px; padding-right: 0px; padding-bottom: 0px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines" role="presentation"><div role="presentation" style="position: relative; outline: none;"><div class="CodeMirror-measure"><pre><span>xxxxxxxxxx</span></pre></div><div class="CodeMirror-measure"/><div style="position: relative; z-index: 1;"/><div class="CodeMirror-code" role="presentation" style=""><div class="CodeMirror-activeline" style="position: relative;"><div class="CodeMirror-activeline-background CodeMirror-linebackground"/><div class="CodeMirror-gutter-background CodeMirror-activeline-gutter" style="left: 0px; width: 0px;"/><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">enum</span> <span class="cm-keyword">import</span> <span class="cm-variable">auto</span></span></pre></div><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">os</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">typing</span> <span class="cm-keyword">import</span> <span class="cm-variable">Generator</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">import</span> <span class="cm-variable">pytest</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">from</span> <span class="cm-variable">playwright</span>.<span class="cm-property">sync_api</span> <span class="cm-keyword">import</span> <span class="cm-variable">Playwright</span>, <span class="cm-variable">Page</span>, <span class="cm-variable">APIRequestContext</span>, <span class="cm-variable">expect</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_API_TOKEN</span> <span class="cm-operator">=</span> <span class="cm-variable">os</span>.<span class="cm-property">getenv</span>(<span class="cm-string">"GITHUB_API_TOKEN"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">assert</span> <span class="cm-variable">GITHUB_API_TOKEN</span>, <span class="cm-string">"GITHUB_API_TOKEN is not set"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_USER</span> <span class="cm-operator">=</span> <span class="cm-variable">os</span>.<span class="cm-property">getenv</span>(<span class="cm-string">"GITHUB_USER"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">assert</span> <span class="cm-variable">GITHUB_USER</span>, <span class="cm-string">"GITHUB_USER is not set"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-variable">GITHUB_REPO</span> <span class="cm-operator">=</span> <span class="cm-string">"test"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">api_request_context</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">playwright</span>: <span class="cm-variable">Playwright</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">) <span class="cm-operator">-></span> <span class="cm-variable">Generator</span>[<span class="cm-variable">APIRequestContext</span>, <span class="cm-keyword">None</span>, <span class="cm-keyword">None</span>]:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">headers</span> <span class="cm-operator">=</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># We set this header per GitHub guidelines.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"Accept"</span>: <span class="cm-string">"application/vnd.github.v3+json"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Add authorization token to all requests.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Assuming personal access token available in the environment.</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"Authorization"</span>: <span class="cm-string">f"token </span>{<span class="cm-variable">GITHUB_API_TOKEN</span>}<span class="cm-string">"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">request_context</span> <span class="cm-operator">=</span> <span class="cm-variable">playwright</span>.<span class="cm-property">request</span>.<span class="cm-property">new_context</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">base_url</span><span class="cm-operator">=</span><span class="cm-string">"https://api.github.com"</span>, <span class="cm-variable">extra_http_headers</span><span class="cm-operator">=</span><span class="cm-variable">headers</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> )</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">yield</span> <span class="cm-variable">request_context</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">request_context</span>.<span class="cm-property">dispose</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-meta">@pytest</span>.<span class="cm-property">fixture</span>(<span class="cm-variable">scope</span><span class="cm-operator">=</span><span class="cm-string">"session"</span>, <span class="cm-variable">autouse</span><span class="cm-operator">=</span><span class="cm-keyword">True</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">create_test_repository</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">api_request_context</span>: <span class="cm-variable">APIRequestContext</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;">) <span class="cm-operator">-></span> <span class="cm-variable">Generator</span>[<span class="cm-keyword">None</span>, <span class="cm-keyword">None</span>, <span class="cm-keyword">None</span>]:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># Before all</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">new_repo</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">post</span>(<span class="cm-string">"/user/repos"</span>, <span class="cm-variable">data</span><span class="cm-operator">=</span>{<span class="cm-string">"name"</span>: <span class="cm-variable">GITHUB_REPO</span>})</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">new_repo</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">yield</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-comment"># After all</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">deleted_repo</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">delete</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">deleted_repo</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span class="cm-keyword">def</span> <span class="cm-def">test_should_create_bug_report</span>(<span class="cm-variable">api_request_context</span>: <span class="cm-variable">APIRequestContext</span>) <span class="cm-operator">-></span> <span class="cm-keyword">None</span>:</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">data</span> <span class="cm-operator">=</span> {</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"title"</span>: <span class="cm-string">"[Bug] report 1"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">"body"</span>: <span class="cm-string">"Bug description"</span>,</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> }</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">new_issue</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">post</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">/issues"</span>, <span class="cm-variable">data</span><span class="cm-operator">=</span><span class="cm-variable">data</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> )</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">new_issue</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp=""> </span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issues</span> <span class="cm-operator">=</span> <span class="cm-variable">api_request_context</span>.<span class="cm-property">get</span>(<span class="cm-string">f"/repos/</span>{<span class="cm-variable">GITHUB_USER</span>}<span class="cm-string">/</span>{<span class="cm-variable">GITHUB_REPO</span>}<span class="cm-string">/issues"</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issues</span>.<span class="cm-property">ok</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issues_response</span> <span class="cm-operator">=</span> <span class="cm-variable">issues</span>.<span class="cm-property">json</span>()</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-variable">issue</span> <span class="cm-operator">=</span> <span class="cm-builtin">list</span>(</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-builtin">filter</span>(<span class="cm-keyword">lambda</span> <span class="cm-variable">issue</span>: <span class="cm-variable">issue</span>[<span class="cm-string">"title"</span>] <span class="cm-operator">==</span> <span class="cm-string">"[Bug] report 1"</span>, <span class="cm-variable">issues_response</span>)</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> )[<span class="cm-number">0</span>]</span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issue</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"> <span class="cm-keyword">assert</span> <span class="cm-variable">issue</span>[<span class="cm-string">"body"</span>] <span class="cm-operator">==</span> <span class="cm-string">"Bug description"</span></span></pre><pre class=" CodeMirror-line " role="presentation"><span role="presentation" style="padding-right: 0.1px;"><span cm-text="" cm-zwsp="">