一言でいうと
Djangoで作成したアプリにSeleniumを使ったテストを追加しました!
これまでの経緯
「画像でゴミ分類!」という画像を入れたらそれが何ゴミか分類してくれるwebアプリを作成しました。いったん完成していたのですが、テストが大事とかテスト駆動開発いいよねという記事に感化されてこのアプリにもテストを導入します。
<これまでの記事一覧>
- 「画像でゴミ分類!」アプリ作成日誌day1~データセットの作成~
- 「画像でゴミ分類!」アプリ作成日誌day2~VGG16でFine-tuning~
- 「画像でゴミ分類!」アプリ作成日誌day3~Djangoでwebアプリ化~
- 「画像でゴミ分類!」アプリ作成日誌day4~Bootstrapでフロントエンドを整える~
- 「画像でゴミ分類!」アプリ作成日誌day5~Bootstrapでフロントエンドを整える2~
- 「画像でゴミ分類!」アプリ作成日誌day6~ディレクトリ構成の修正~
- 「画像でゴミ分類!」アプリ作成日誌day7~サイドバーのスライドメニュー化~
- 「画像でゴミ分類!」アプリ作成日誌day8~herokuデプロイ~
テスト手法
Seleniumを使ったUIテストを行いたいので、DjangoのLiveServerTestCaseを利用します。
Jupyterで動作確認
Seleniumについてまだ勉強中なので、いきなりwebアプリ上に書くのではなく、Jupyterで挙動を確認しながらコードを書いていきます。
まずは、chromeを起動しましょう
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
DRIVER_PATH = r'chromedriver.exeのパス'
# ブラウザの起動
driver = webdriver.Chrome(executable_path=DRIVER_PATH)
そして開発用サーバーにアクセス。ここは開発用サーバーを立ち上げておく必要があります。
driver.get('http://localhost:8000/garbage')
まずは、タイトルを取得してみましょう
driver.title
# '画像でゴミ分類!'
次に、要素を取得してみましょう
driver.find_element_by_class_name("sample-img").get_attribute('outerHTML')
# '<img src="/static/garbage/media/gif/waiting.278078bec7bd.gif" alt="画像1" class="mt-3 sample-img waiting" id="waiting-gif">'
outerHTMLを見ると無事取得できていることがわかります。ただ、実はこのクラスに所属するものは複数あるので、そんなときはfind_elements_by_class_name
と複数形で使うとリストで取得できます。
len(driver.find_elements_by_class_name("sample-img"))
# 3
では画像をクリックする処理を書きます
driver.find_elements_by_class_name("sample-img")[1].click()
簡単ですね。
今度は遷移先のページでタグを取得してみます。
num_tr = len(driver.find_elements(By.XPATH, '//tr'))
書き方がちょっと変わりましたが、XML Pathで要素を指定しているだけで、やってることは同じですね。
これを用いてテストするには以下のようにすればいいです。
assert 6 == num_tr, "表の行数が一致しません"
では、一度トップページに戻って文字検索時のテストを書いていきます。
まずは、検索ボックスを取得してみましょう
driver.get('http://localhost:8000/garbage')
driver.find_elements(By.XPATH, '//input[@name="word"]')
# [<selenium.webdriver.remote.webelement.WebElement (session="d795b92e9edbbd43c50a2be08e0b75e1", element="54b9e56a-2624-4a33-9f5f-be8fe77a6e5e")>,
# <selenium.webdriver.remote.webelement.WebElement (session="d795b92e9edbbd43c50a2be08e0b75e1", element="1ccd154e-326f-4e5c-8004-9258ec03bb39")>]
あれ、2つ出てくるのはなんででしょうか。これは、スマホ対応時のサイドバーとPC時の左メニューの分ですね。
PC時の左メニューのほうだけを使います。文字列として針を入力してみましょう。
search_box = driver.find_elements(By.XPATH, '//input[@name="word"]')[1]
search_box.send_keys('針')
そして、Enterキーを押します。この後は先ほどと同じく表の行数をチェックするようにしようかと思います。
search_box.send_keys(Keys.ENTER)
len(driver.find_elements(By.XPATH, '//tr'))
# 14
testコードの追加
これまで書いたコードをwebアプリ上に移植します。
まず、test.pyを削除してその代わりにtests/test_views.pyを追加します。
まずはおまじないです。LiveServerTestCaseクラスをオーバーライドしてドライバーなどの場所を教えます。
from django.test import LiveServerTestCase
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
class TestClickSample(LiveServerTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.selenium = WebDriver(executable_path=r'garbage\tests\chromedriver.exe')
@classmethod
def tearDownClass(cls):
cls.selenium.quit()
super().tearDownClass()
まずは、ページを開いてタイトルがあってるかを確認をしておきましょう。
def test_open_page(self):
"""ページを開いてタイトルがあってるかを確認
"""
self.selenium.get('http://localhost:8000/garbage')
self.assertEquals("画像でゴミ分類!", self.selenium.title)
次にサンプル画像をクリックしたときの処理を記述して、モデルの挙動が正しいかを確認できるようにします。
def test_click_sample(self):
"""モデルの動きが正常かを確認
サンプル画像をクリックした際の挙動を確認
"""
self.selenium.get('http://localhost:8000/garbage')
self.selenium.find_elements_by_class_name("sample-img")[1].click()
num_tr = len(self.selenium.find_elements(By.XPATH, '//tr'))
self.assertEquals(6, num_tr)
最後に文字検索時の挙動をテストします。
def test_search(self):
"""文字検索時の挙動をテスト
"""
self.selenium.get('http://localhost:8000/garbage')
search_box = self.selenium.find_elements(By.XPATH, '//input[@name="word"]')[1]
search_box.send_keys('針')
search_box.send_keys(Keys.ENTER)
num_tr = len(self.selenium.find_elements(By.XPATH, '//tr'))
self.assertEquals(14, num_tr)
テスト実行してみましょう。
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
----------------------------------------------------------------------
Ran 3 tests in 10.380s
OK
無事テストが通ったようです!
参考文献
- 大高 隆(2019)『動かして学ぶ!Python Django開発入門』翔泳社
- Djangoを始めよう! 〜チュートリアル⑤〜
- Seleniumで要素を選択する方法まとめ