[Python]Page Object Modelパターンを用いたPlaywright E2Eテスト記述方法 メモ

* Page Object Modelパターンを利用して、PlaywrightでE2Eテストを記述する方法についてメモしておく。
    * Google検索を行い、検索結果を確認するテスト([過去にSeleniumで書いた内容](https://qiita.com/KWS_0901/items/9afd8f70c9f863436432))をPlaywrightで書いてみる。

## POM(Page Object Model)とは

* ページをクラスオブジェクトとして扱うブラウザ自動化テストのデザインパターンの一つ。

* 主な概念

  * テストクラス：対象ページのテストケース。
  * ページオブジェクト
    * 各Webページオブジェクトを作成することを目的としたクラス。
    * テスト用コードとWebページアクセス用コードを分離。
  * ロケータ：ページ要素を取得させる。

* 利点

  * 可読性の高いテストケースを書くことができる。
  * 複数のテストケース間で共有できる再利用可能なコードを作ることができる※コード重複を防ぐことができる。

  

## 事前準備

* ライブラリ類インストール

  ```shell
  pip install playwright
  playwright install
  pip install pytest-playwright
  ```

* テストレポートツール`allure`インストール

  * 筆者はWindowsを利用しているため、パッケージ管理ツール`scoop`を使用する。

    * `scoop`インストール方法は[こちら](https://qiita.com/rhene/items/d8a0c0c7d637904e14da)を参照。

    ```
    scoop install allure
    pip install allure-pytest
    ```

    

## 構成

```
root	- 	TestBase.py
		|_	test_Google.py
		|_	pages	- 	__init__.py
					|_	google_page.py
```



## コード

※ページクラスとテストクラスのみでロケータクラスは用意していない。

* `pages/google_page.py`

  ```python
  class BasePage(object):
  
      def __init__(self, page):
          self.page = page
  
      def navigate(self, url):
          self.page.goto(url)
  
  
  class SearchPage(BasePage):
  
      def is_title_matches(self):
  
          return "Google" in self.page.title()
  
      def search(self, text):
          self.page.fill('input[name="q"]', text)
          self.page.press('input[name="btnK"]', "Enter")
  
  
  class SearchResultsPage(BasePage):
  
      def is_results_found(self):
          selector = '#result-stats'
          content = self.page.locator(
              selector).text_content()
          return content != None
  
  ```

* `TestBase.py`:テストで共通する処理を集約する。

  ```python
  from playwright.sync_api import Page
  import pytest
  
  
  # 共通処理を行うベーステストクラス
  class TestBase():
      @pytest.fixture(autouse=True)
      def setup(self, page: Page):
          self.page = page
  
      def tearDown(self):
          if (self.page != None):
              print("Cleanup of test environment")
              self.page.close()
  
  ```

* `test_Google.py`:テストケースクラス。

  ```python
  from pages.google_page import SearchPage, SearchResultsPage
  import allure
  from TestBase import TestBase
  
  
  class TestGoogle(TestBase):
      @allure.title('「Playwright Python」でGoogle検索し、検索結果を取得できているか確認する')
      def test_search_in_google(self):
          search_page = SearchPage(self.page)
          search_page.navigate("https://google.com")
          # タイトル検証
          assert search_page.is_title_matches() == True
  
          search_page.search("Playwright Python")
          search_result_page = SearchResultsPage(self.page)
          # 検索結果(検索件数を取得できているか)検証
          assert search_result_page.is_results_found() == True
  
  ```

  

## 動作確認

* テスト実行

  ```shell
  pytest test_Google.py --alluredir=allure_results
  =========================================================== test session starts ===========================================================
  platform win32 -- Python 3.8.3, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
  rootdir: ...
  collected 1 item
  
  test_Google.py .                                                                                                                     [100%]
  
  ============================================================ 1 passed in 5.04s ============================================================
  ```

  

* テストレポート確認

  ```shell
  allure serve allure_results
  ```

  ![allure_report.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/2320eee0-1ade-57b7-54ae-fd57bab38f58.png)




## 参考情報

* [Page Object Models](https://playwright.dev/python/docs/pom/)
* [Page Object Modeling with Python and Playwright](https://medium.com/analytics-vidhya/page-object-modeling-with-python-and-playwright-3cbf259eedd3)