Robot Framework + SeleniumLibraryで、Headless Firefoxを動かす

この記事は「Robot Framework Advent Calendar 2017 - Qiita」の6日目の記事です。

昨日はRobot Framework + SeleniumLibraryで Headless Chromeを動かしました。
Robot Framework + SeleniumLibrary + Selenium3.8以降でHeadless Chromeを動かす - メモ的な思考的な

 
そんな中、FirefoxでもHeadlessが実装されたと聞きました。LinuxはFirefox55から、Mac/WindowsはFirefox56から動作するようです。
ヘッドレスモード - Mozilla | MDN

 
そこで今回、Robot Framework + SeleniumLibraryで Headless Firefoxを動かしてみます。

 
目次

 

環境

 

HeadlessなFirefoxを起動するには

MDNのドキュメントに従い、

${binary} =  Evaluate  sys.modules['selenium.webdriver.firefox.firefox_binary'].FirefoxBinary()  sys
Call Method  ${binary}  add_command_line_options  -headless
Create Webdriver  Firefox  firefox_binary=${binary}

と実装してみましたが、非HeadlessなFirefoxが起動しました。

 
仕方ないので他の方法を探してみたところ、以下のページに FirefoxOptions を使う方法が記載されていました。
selenium - Running headless Firefox WebDriver on Jenkins (Windows OS) - Stack Overflow

そのため、

${options} =  Evaluate  sys.modules['selenium.webdriver.firefox.options'].Options()  sys
Call Method  ${options}  add_argument  -headless
Create Webdriver  Firefox  firefox_options=${options}

としたところ、HeadlessなFirefoxが起動しました。

 
さらに、selenium 3.8.0より、Firefoxのoptions.pyにも set_headless() メソッドが追加されていました。

 
そのため、selenium 3.8.0からは、以下の方法でもHeadless Firefoxを起動できます。

${options} =  Evaluate  sys.modules['selenium.webdriver.firefox.options'].Options()  sys
Call Method  ${options}  set_headless
Create Webdriver  Firefox  firefox_options=${options}

 

Headless Firefoxを試してみる

Headless Chromeと同様、Headless Firefoxを使って、GooglePythonを検索してスクリーンショットを撮ってみます。

*** Settings ***
Library  SeleniumLibrary


*** Keywords ***
set_headlessメソッドがない場合はテストをパスする
    ${version} =  Evaluate  selenium.__version__  selenium
    ${selenium_version} =  Evaluate  importlib.import_module('distutils.version').LooseVersion($version)  importlib
    # set_headlessメソッドは、python seleniumの3.8.0以降に実装された
    ${min_version} =  Evaluate  importlib.import_module('distutils.version').LooseVersion('3.8.0')  importlib

    Pass Execution If  $selenium_version < $min_version  seleniumが${min_version}以上でないと動作しません


set_headlessメソッドを使ってHeadlessなFirefoxを起動する
    set_headlessメソッドがない場合はテストをパスする
    ${options} =  Evaluate  sys.modules['selenium.webdriver.firefox.options'].Options()  sys
    Call Method  ${options}  set_headless
    Create Webdriver  Firefox  firefox_options=${options}


add_argumentメソッドを使ってHeadlessなFirefoxを起動する
     ${options} =  Evaluate  sys.modules['selenium.webdriver.firefox.options'].Options()  sys
     Call Method  ${options}  add_argument  -headless
     Create Webdriver  Firefox  firefox_options=${options}


GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する
    # Googleのトップ画面を開く
    Go To  https://www.google.co.jp/

    # タイトルにGoogleが含まれていることを確認する
    ${page_title} =  Get Title
    Should Contain  ${page_title}  Google

    # 検索後を入力して送信する
    Input Text  name=q  Python
    Press Key  name=q  \\13

    # Ajax遷移のため、適当に2秒待つ
    Sleep  2sec

    # タイトルにPythonが含まれていることを確認する
    ${result_title} =  Get Title
    Should Contain  ${result_title}  Python

    # スクリーンショットを撮る
    Capture Page Screenshot  filename=result_google_python.png

    # ログを見やすくするために改行を入れる
    Log To Console  ${SPACE}

    # 検索結果を表示する
    @{web_elements} =  Get Webelements  css=h3 > a
    :For  ${web_element}  In  @{web_elements}
    \  ${text} =  Get Text  ${web_element}
    \  Log To Console  ${text}
    \  ${href} =  Call Method  ${web_element}  get_attribute  href
    \  Log To Console  ${href}

    # ブラウザを終了する
    Close Browser


*** TestCases ***
add_argumentメソッドを使ってHeadlessなFirefoxを起動しテストする
    add_argumentメソッドを使ってHeadlessなFirefoxを起動する
    GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する

set_headlessメソッドを使ってHeadlessなFirefoxを起動しテストする
    set_headlessメソッドを使ってHeadlessなFirefoxを起動する
    GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する

 

実行結果

実行ログ

両方ともテストをパスしました。

$ robot test_google_with_headless_firefox.robot 
=============================
Test Google With Headless Firefox
=============================
add_argumentメソッドを使ってHeadlessなFirefoxを起動しテストする       . 
Python - ウィキペディア
https://ja.wikipedia.org/wiki/Python
Pythonとは - python.jp
https://www.python.jp/about/
専門知識いらず!Pythonとは?言語の特徴から網羅的に徹底解説 | 侍 ...
https://www.sejuku.net/blog/7720
Python基礎講座(1 Pythonとは) - Qiita
https://qiita.com/Usek/items/ff4d87745dfc5d9b85a4
Python 13370 posts - Qiita
https://qiita.com/tags/Python
Python入門
http://www.tohoho-web.com/python/
Python | プログラミングの入門なら基礎から学べるProgate[プロゲート]
https://prog-8.com/languages/python
Python 3を使うべきでない場合(なんてない) | プログラミング | POSTD
http://postd.cc/case-python-3/
Pythonとは?何に使えるの?Pythonの特徴や使い道を…|Udemy メディア
https://udemy.benesse.co.jp/development/python.html
Pythonは今のうちに習得すべき?『スラスラわかるPython』著者・岩崎さんと ...
https://codezine.jp/article/detail/10329
add_argumentメソッドを使ってHeadlessなFirefoxを起動しテストする       | PASS |
----------------------------------------
set_headlessメソッドを使ってHeadlessなFirefoxを起動しテストする       . 
Python - ウィキペディア
https://ja.wikipedia.org/wiki/Python
Pythonとは - python.jp
https://www.python.jp/about/
専門知識いらず!Pythonとは?言語の特徴から網羅的に徹底解説 | 侍 ...
https://www.sejuku.net/blog/7720
Python基礎講座(1 Pythonとは) - Qiita
https://qiita.com/Usek/items/ff4d87745dfc5d9b85a4
Python 13370 posts - Qiita
https://qiita.com/tags/Python
Python入門
http://www.tohoho-web.com/python/
Python | プログラミングの入門なら基礎から学べるProgate[プロゲート]
https://prog-8.com/languages/python
Python 3を使うべきでない場合(なんてない) | プログラミング | POSTD
http://postd.cc/case-python-3/
Pythonとは?何に使えるの?Pythonの特徴や使い道を…|Udemy メディア
https://udemy.benesse.co.jp/development/python.html
Pythonは今のうちに習得すべき?『スラスラわかるPython』著者・岩崎さんと ...
https://codezine.jp/article/detail/10329
set_headlessメソッドを使ってHeadlessなFirefoxを起動しテストする       | PASS |
----------------------------------------
Test Google With Headless Firefox       | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
=============================

 

スクリーンショット

Headless Firefoxですが、問題なくスクリーンショットが撮れています。

f:id:thinkAmi:20171205211805p:plain

 
昨日のChromeと比べて撮影されている範囲が広くなっています。スクリーンショットはブラウザにより違いますね。

 

ソースコード

GitHubに上げました。 selenium_firefox_headless_sample ディレクトリの test_google_with_headless_firefox.robot ファイルが今回のものです。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples

Robot Framework + SeleniumLibrary + Selenium3.8以降でHeadless Chromeを動かす

この記事は「Robot Framework Advent Calendar 2017 - Qiita」の5日目の記事です。

 
以前、 Selenium2Library 時代にHeadless Chromeを使ってみました。
Robot FrameworkでHeadless Chromeを使ってみた - メモ的な思考的な

 
その後、

  • SeleniumLibrary にライブラリ名が変わった
  • Seleniumのバージョンが上がって、便利なメソッドができていた

となったため、改めて Robot Framework + SeleniumLibraryで Headless Chromeを動かしてみます。

 
目次

 

環境

  • Python 3.6.3
  • Chrome 62.0.3202.94
  • chromedriver 2.33
  • RobotFramework 3.0.2
  • SeleniumLibrary 3.0.0
  • selenium 3.8.0

 

set_headless() メソッドを使う

Selenium 3.8より、 selenium.webdriver.chrome.options.pyset_selenium() メソッドが追加されていました。
https://github.com/SeleniumHQ/selenium/blob/selenium-3.8.0/py/selenium/webdriver/chrome/options.py#L161

ソースコードを読むと、ChromeOptiosにheadlessを設定してくれるようなので、今回はこれを使ってみます。

 

実装について

Headless Chromeを set_headless() メソッドを使って起動する

Headless Chromeset_headless() メソッドを使って起動するには、add_argment()メソッドと同じくChromeOptionsクラスを使い、

を行います。

# ChromeOptionsクラスをインスタンス化
${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys

# ChromeOptionsインスタンスの set_headless メソッドを実行
call method  ${options}  set_headless

create webdriver  Chrome  chrome_options=${options}

 

全体

add_argment() と set_headless() の両パターンを書いてみました。

*** Settings ***
Library  SeleniumLibrary


*** Keywords ***
set_headlessメソッドがない場合はテストをパスする
    ${version} =  evaluate  selenium.__version__  selenium
    ${selenium_version} =  evaluate  importlib.import_module('distutils.version').LooseVersion($version)  importlib
    # set_headlessメソッドは、python seleniumの3.8.0以降に実装された
    ${min_version} =  evaluate  importlib.import_module('distutils.version').LooseVersion('3.8.0')  importlib

    Pass Execution If  $selenium_version < $min_version  seleniumが${min_version}以上でないと動作しません


set_headlessメソッドを使ってHeadlessなChromeを起動する
    set_headlessメソッドがない場合はテストをパスする
    ${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys
    Call Method  ${options}  set_headless
    Create Webdriver  Chrome  chrome_options=${options}


add_argumentメソッドを使ってHeadlessなChromeを起動する
    # https://groups.google.com/d/msg/robotframework-users/gPsiVaMo19A/cBRH7mr2BAAJ
    ${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys
    Call Method  ${options}  add_argument  --headless
    Create Webdriver  Chrome  chrome_options=${options}


GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する
    # 以下のコードをRobot Framework風にした
    # http://qiita.com/orangain/items/db4594113c04e8801aad

    # Googleのトップ画面を開く
    Go To  https://www.google.co.jp/

    # タイトルにGoogleが含まれていることを確認する
    ${page_title} =  get title
    Should Contain  ${page_title}  Google

    # 検索後を入力して送信する
    Input Text  name=q  Python
    # Robot FrameworkではEnterキーは\\13になる
    # https://github.com/robotframework/Selenium2Library/issues/4
    Press Key  name=q  \\13

    # Ajax遷移のため、適当に2秒待つ
    Sleep  2sec

    # タイトルにPythonが含まれていることを確認する
    ${result_title} =  get title
    should contain  ${result_title}  Python

    # スクリーンショットを撮る
    Capture Page Screenshot  filename=result_google_python2.png

    # ログを見やすくするために改行を入れる
    Log To Console  ${SPACE}

    # 検索結果を表示する
    # ForでElementを回したかったことから、WebElementを取得し、そのAPIを利用する
    # http://robotframework.org/Selenium2Library/Selenium2Library.html#Get%20Webelements
    # https://stackoverflow.com/a/42508200
    @{web_elements} =  Get Webelements  css=h3 > a
    :For  ${web_element}  In  @{web_elements}
    \  ${text} =  Get Text  ${web_element}
    \  Log To Console  ${text}
    # 以下を参考に、WebElementからattribute(href)を取得
    # https://groups.google.com/d/msg/robotframework-users/xx3KYxpDu_w/0hyulqKPKQAJ
    # http://seleniumhq.github.io/selenium/docs/api/py/webdriver_remote/selenium.webdriver.remote.webelement.html#selenium.webdriver.remote.webelement.WebElement.get_attribute
    \  ${href} =  Call Method  ${web_element}  get_attribute  href
    \  Log To Console  ${href}

    # ブラウザを終了する
    Close Browser


*** TestCases ***
add_argumentメソッドを使ってHeadlessなChromeを起動しテストする
    add_argumentメソッドを使ってHeadlessなChromeを起動する
    GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する

set_headlessメソッドを使ってHeadlessなChromeを起動しテストする
    set_headlessメソッドを使ってHeadlessなChromeを起動する
    GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する

 

実行結果

実行ログ

両方ともテストをパスしました。

$ robot test_google_with_headless_chrome.robot 
==============================
Test Google With Headless Chrome
==============================
add_argumentメソッドを使ってHeadlessなChromeを起動しテストする        . 
Python - ウィキペディア
https://ja.wikipedia.org/wiki/Python
Pythonとは - python.jp
https://www.python.jp/about/
専門知識いらず!Pythonとは?言語の特徴から網羅的に徹底解説 | 侍 ...
https://www.sejuku.net/blog/7720
Python基礎講座(1 Pythonとは) - Qiita
https://qiita.com/Usek/items/ff4d87745dfc5d9b85a4
Python 13370 posts - Qiita
https://qiita.com/tags/Python
Python入門
http://www.tohoho-web.com/python/
Python | プログラミングの入門なら基礎から学べるProgate[プロゲート]
https://prog-8.com/languages/python
Pythonとは?何に使えるの?Pythonの特徴や使い道を…|Udemy メディア
https://udemy.benesse.co.jp/development/python.html
Python 3を使うべきでない場合(なんてない) | プログラミング | POSTD
http://postd.cc/case-python-3/
Pythonは今のうちに習得すべき?『スラスラわかるPython』著者・岩崎さんと ...
https://codezine.jp/article/detail/10329
add_argumentメソッドを使ってHeadlessなChromeを起動しテストする        | PASS |
-----------------------------------------
set_headlessメソッドを使ってHeadlessなChromeを起動しテストする        . 
Python - ウィキペディア
https://ja.wikipedia.org/wiki/Python
Pythonとは - python.jp
https://www.python.jp/about/
専門知識いらず!Pythonとは?言語の特徴から網羅的に徹底解説 | 侍 ...
https://www.sejuku.net/blog/7720
Python基礎講座(1 Pythonとは) - Qiita
https://qiita.com/Usek/items/ff4d87745dfc5d9b85a4
Python 13370 posts - Qiita
https://qiita.com/tags/Python
Python入門
http://www.tohoho-web.com/python/
Python | プログラミングの入門なら基礎から学べるProgate[プロゲート]
https://prog-8.com/languages/python
Pythonとは?何に使えるの?Pythonの特徴や使い道を…|Udemy メディア
https://udemy.benesse.co.jp/development/python.html
Python 3を使うべきでない場合(なんてない) | プログラミング | POSTD
http://postd.cc/case-python-3/
Pythonは今のうちに習得すべき?『スラスラわかるPython』著者・岩崎さんと ...
https://codezine.jp/article/detail/10329
set_headlessメソッドを使ってHeadlessなChromeを起動しテストする        | PASS |
-----------------------------------------
Test Google With Headless Chrome        | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
==============================

 

スクリーンショット

Headless Chromeですが、問題なくスクリーンショットが撮れています。

f:id:thinkAmi:20171205210839p:plain

 

ソースコード

GitHubに上げました。 selenium_chrome_headless_sample ディレクトリの test_google_with_headless_chrome.robot ファイルが今回のものです。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples

Robot Frameworkのライブラリのドキュメントを読んだ時に悩んだこと

この記事は「Robot Framework Advent Calendar 2017 - Qiita」の4日目の記事です。

 
Robot Frameworkはドキュメントが充実しています。そのため、たいていのことはドキュメントを読めば解決します。

例えば、Robot Frameworkの標準ライブラリで使えるキーワードは、以下のドキュメントにて解説されています。

 
また、ライブラリのドキュメントは、決まったフォーマットで作成されています。これは Libdoc (Library documentation tool) にて生成されたものです。
robotframework/Libdoc.rst at master · robotframework/robotframework

ただ、最初はLibdocドキュメントの読み方が分からなかったため、その時のメモを残します。

 
今回は例として、Builtinライブラリのキーワード Should Be Equal を使います。

f:id:thinkAmi:20171108221306p:plain

 
キーワードのドキュメントが表形式で作成されており、左から順に

  • Keyword
  • Arguments
  • Documentation

となります。

 

Keyword

Robot Frameworkのテストコードに記載するキーワードです。

例では Should Be Equal になります。

 

Arguments

キーワードの引数です。

  • 引数名
  • 引数名=デフォルト値

のどちらかになります。前者であれば必須の引数、後者は任意の引数となります。

 
例では、

  • first
  • second

は必須です。一方、

  • msg=None
  • values=True
  • ignore_case=False

は任意です。

 

Documentation

キーワードの説明や使い方、サンプルなどが記載されています。

サンプルはスペース・パイプ区切り方式で書かれています。そのため、スペース区切り形式を使っている場合は、列の区切り(|)を2つのスペースへと脳内変換します。

 
例えば、

|Should Be Equal|${x}|expected|

をスペース区切り形式で使う場合、

Should Be Equal  ${x}  expected

と書きます。

 
Libdocのフォーマットは決まっているため、ライブラリのドキュメントの読み方も慣れやすいです。

Mac/Windowsでゼロから始めるRobot Framework + SeleniumLibrary

この記事は「Robot Framework Advent Calendar 2017 - Qiita」の3日目の記事です。

 
Robot Frameworkはドキュメントが充実しているため、きちんと読めばゼロから始められます。

ただ、ドキュメント量が多いため、最初のテストコードを書くまでに時間がかかるかもしれません。

そのため、今回はドキュメントへの参照リンクを付けながら、Mac/Windowsでゼロから始めるRobot Framework + SeleniumLibraryのテストコードを書いていきます。

目次

 

環境構築

Robot Framework + Selenium Library を使うためには、以下が必要です。

 
順番に準備します。

 

Python3のインストール

Robot Frameworkを動かすためには、Pythonが必要です。

今のところRobot FrameworkはPython2/3のどちらでも動きますが、今回はPython3を使います。

MacWindowsPythonをインストールする方法は、Python Boot Campのドキュメントに書かれていますので、そちらを参照してインストールしてください。
1. Pythonをはじめる前に — Python Boot Camp Text 2016.04.28 ドキュメント

 

今回使用するディレクトリの準備

任意の場所にディレクトリを用意しておきます。

今回は hello_robot ディレクトリの中で作業します。

# ディレクトリの作成
mkdir hello_robot

# 移動
cd hello_robot

 

Pythonのvenv環境の構築

先ほどインストールしたPython環境にRobot Frameworkを入れてもよいです。

ただ、他でもPython環境を使う可能性があるため、今回はvenvを使ってPythonの仮想環境にRobot Frameworkを入れることにします。

Python Boot Campのテキストを参考に、venv環境を作り、有効化(activate)しておきます。
6.2. venvとは - 6. サードパーティ製パッケージと venv — Python Boot Camp Text 2016.04.28 ドキュメント

 

# venv環境を、rfenvという名前で作成
python -m venv rfenv

# 有効化
## Mac
$ source rfenv/bin/activate
## Windows
>rfenv\Scripts\activate.bat


# 有効化されている
(rfenv) $ 
(rfenv) >

 

Robot FrameworkとSeleniumLibraryをインストール

本体をインストールします。

(rfenv) pip install robotframework-seleniumlibrary

Collecting robotframework-seleniumlibrary
  Using cached robotframework_seleniumlibrary-3.0.0-py2.py3-none-any.whl
Collecting robotframework>=2.8.7 (from robotframework-seleniumlibrary)
  Using cached robotframework-3.0.2.tar.gz
Collecting selenium>=2.53.6 (from robotframework-seleniumlibrary)
  Using cached selenium-3.8.0-py2.py3-none-any.whl
Installing collected packages: robotframework, selenium, robotframework-seleniumlibrary
  Running setup.py install for robotframework ... done
Successfully installed robotframework-3.0.2 robotframework-seleniumlibrary-3.0.0 selenium-3.8.0

 

ChromeDriverのインストール

今回はブラウザとしてChromeを使います。

ChromeSeleniumから操作するために、ChromeDriverをインストールします。現時点の最新版は、2.33です。

Macの場合はHomebrewでインストールします。

$ brew install chromedriver

 
Windowsの場合は、ChromeDriverをダウンロードします。
Downloads - ChromeDriver - WebDriver for Chrome

今回は簡単に使うだけなので、hello_robot ディレクトリに、解凍したchromedriver.exe を入れておきます。

 
以上で環境構築は完了です。

 

テストコードの作成

今回は以下のPython + Seleniumのテストコードの内容をRobot Frameworkで書いていきます。

from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
import unittest

class TestGoogle(unittest.TestCase):
    def test_GoogleでSeleniumLibraryを検索する(self):
        browser = Chrome()
        browser.get('https://google.co.jp')
        query_input = browser.find_element_by_name('q')
        query_input.send_keys('SeleniumLibrary' + Keys.ENTER)
        # 結果出力と検証
        links = browser.find_elements_by_css_selector('h3 > a')
        for link in links:
            print(link.text)
        self.assertEqual(len(links), 10)
        browser.quit()

 

テストコードのファイル形式の選択と作成

Robot Frameworkのテストコードのファイル形式については、公式ドキュメントの以下のページに記載があります。

 
以下のファイル形式が使えます。

  • HTML
  • TSV
  • プレーンテキスト (.robot, .txt)
  • reST

 
今回はWeb上でよく見かける、プレーンテキスト(拡張子は .robot )を使います。

なお、プレーンテキストの場合、

  • スペース区切り方式
  • スペース・パイプ区切り方式

のどちらかを選べますが、今回は前者のスペース区切り方式を選択します。

 
また、Robot Frameworkでは、テストデータテーブルという概念でテストコードを書きます。

 
テストデータテーブルは、以下の4つの区分で構成されます。

  • Settings
  • Variables
  • Test Cases
  • Keywords

それぞれの使い方については、後述します。

 
以上より、今回はテストコードをtest_selenium.robot として作成し、hello_robot ディレクトリの中に保存します。

ここまでのディレクトリ構成は以下の通りです。

hello_robot/
├── test_selenium.robot
├── chromedriver.exe (Windowsの場合のみ)
└── rfenv/

 
 

参照するライブラリの指定

Pythonのコードでは import でライブラリ(モジュール)を指定しています。

Robot Frameworkでも参照するライブラリを指定します。

今回はSettingsにて Library 設定を記述します。

*** Settings ***
Library  SeleniumLibrary

 

テストコードの実装

基本的な構文は以下に記載があります。

 

TestCaseとテストケースの記述

まずは、 TestCases とテストケース名を記述します。テストケース名には日本語を使用できます。

*** TestCases ***
GoogleでSeleniumLibraryを検索するテスト

 

テストケースの詳細を実装

続いてSeleniumを操作するキーワードを記述します。

SeleniumLibraryで使えるキーワードは公式ドキュメントを参照します。

まずは

# Chromeを起動する
browser = webdriver.Chrome()
# Googleへ遷移する
browser.get('https://google.co.jp')
# 検索枠に入力してEnterキーを押す
query_input = browser.find_element_by_name('q')
query_input.send_keys('SeleniumLibrary' + Keys.ENTER)

をRobot Frameworkで記述します。

Open Browser  https://google.co.jp  Chrome
Input Text  name=q  SeleniumLibrary
Press Key  name=q  \\13

ちなみに、\\13 については、SeleniumLibraryでEnterキーを押すためのコードです。
http://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Press%20Key

なお、Pythonと異なり、今のところ「SeleniumLibrary + Enterキー」を同時に入力する方法は実装されていません。
https://github.com/robotframework/SeleniumLibrary/issues/498

 
次に、メソッドを実行して変数として受け取る部分です。

links = browser.find_elements_by_css_selector('h3 > a')

Robot Frameworkの変数は以下に記載があります。

今回は、 ${} を使います。

${links} =  Get Webelements  css=h3 > a

 
続いて、forループと検証です。

for link in links:
    print(link.text)
self.assertEqual(len(links), 10)

 
Robot Frameworkでforループを使う場合は、以下に記述があります。

 
これによると、

のようです。

つまり、

:For  ${link}  In  @{links}
    \  ${text} =  Get Text  ${link}

となります。

 
次に、

  • コンソールに結果を出力 (print)
  • リンク数を検証 (self.assertEqual)

をRobot Frameworkで実装します。

これらはRobot Frameworkの組込みライブラリであるBuiltinライブラリにあるため、そちらのドキュメントを参照します。

 
それらは

    \  Log To Console  ${text}
Length Should Be  ${links}  10

となります。

 
最後にブラウザを閉じる(browser.quit())です。

Close Browser

 
これで全体ができました。test_selenium.robot全体のソースコードは以下の通りです。

*** Settings ***
Library  SeleniumLibrary

*** TestCases ***
GoogleでSeleniumLibraryを検索するテスト
    Open Browser  https://google.co.jp  Chrome
    Input Text  name=q  SeleniumLibrary
    Press Key  name=q  \\13
    # 結果出力と検証
    ${links} =  Get Webelements  css=h3 > a
    :For  ${link}  In  @{links}
        \  ${text} =  Get Text  ${link}
        \  Log To Console  ${text}
    Length Should Be  ${links}  10
    Close Browser

 

テストの実行

Python + Seleniumの場合、以下のように実行します。

(rfenv) $ python -m unittest test_selenium.py 
GitHub - robotframework/SeleniumLibrary: Web testing library for ...
SeleniumLibrary - Robot Framework
robotframework-seleniumlibrary 3.0.0rc2 : Python Package Index - PyPI
Python3で動く、Robot Framework の SeleniumLibrary 3.0 のリリース ...
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと ...
robotframework-seleniumlibrary - Demo.wiki - Google Code Archive ...
Maven Repository: org.jboss.test.richfaces-selenium.library
Selenium with Python — Selenium Python Bindings 2 documentation
Selenium IDE: RobotFramework Formatter :: レビュー :: Firefox 向け ...
Selenium - Web Browser Automation
.
----------------------------------------------------------------------
Ran 1 test in 8.426s

OK

 
Robot Frameworkの場合、 robot コマンドで実行します。

(rfenv) $ robot test_selenium.robot 
==================
Hello Selenium                                                                
==================
GoogleでSeleniumLibraryを検索するテスト                               ....
GitHub - robotframework/SeleniumLibrary: Web testing library for ...
SeleniumLibrary - Robot Framework
Python3で動く、Robot Framework の SeleniumLibrary 3.0 のリリース ...
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと ...
robotframework-seleniumlibrary 3.0.0rc2 : Python Package Index - PyPI
robotframework-seleniumlibrary 2.8.1 : Python Package Index
robotframework-seleniumlibrary 3.0.0a1 : Python Package Index
robotframework-seleniumlibrary - Demo.wiki - Google Code Archive ...
Maven Repository: org.jboss.test.richfaces-selenium.library
Selenium with Python — Selenium Python Bindings 2 documentation
GoogleでSeleniumLibraryを検索するテスト                               | PASS |
-------------------------
Hello Selenium                                                        | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
==================
Output:  /path/to/hello_robot/output.xml
Log:     /path/to/hello_robot/log.html
Report:  /path/to/hello_robot/report.html

 
実行後、ログファイルが出力されましたので、それぞれ確認してみます。

log.html

f:id:thinkAmi:20171203091918p:plain

report.html

f:id:thinkAmi:20171203091933p:plain

 

テストコードを修正する

上記のテストコードでは、テストデータテーブルの

  • Variables
  • Keywords

を使っていなかったため、それを使うように修正してみます。

 

Variablesを使う

Variablesは、変数を事前に定義する時に使います。

 
こんな感じで定義します。

*** Variables ***
${検索キーワード}  SeleniumLibrary

 
定義した変数 ${検索キーワード} を使うように修正します。

# Input Text  name=q  SeleniumLibrary
Input Text  name=q  ${検索キーワード}

 

Keywordsを使う

Keywordsは、自作のキーワード(ユーザーキーワード)を作成する時に使います。

 
こんな感じで定義します。

*** Keywords ***
Chromeを起動する
    Open Browser  https://google.co.jp  Chrome

 
定義したキーワードを使うように変更します。

# Open Browser  https://google.co.jp  Chrome
Chromeを起動する

 
テストの全体は以下となりました。

*** Settings ***
Library  SeleniumLibrary

*** Variables ***
${検索キーワード}  SeleniumLibrary


*** Keywords ***
Chromeを起動する
    Open Browser  https://google.co.jp  Chrome


*** TestCases ***
GoogleでSeleniumLibraryを検索するテスト
    Open Browser  https://google.co.jp  Chrome
    Input Text  name=q  SeleniumLibrary
    Press Key  name=q  \\13
    # 結果出力と検証
    ${links} =  Get Webelements  css=h3 > a
    :For  ${link}  In  @{links}
        \  ${text} =  Get Text  ${link}
        \  Log To Console  ${text}
    Length Should Be  ${links}  10
    Close Browser


GoogleでSeleniumLibraryを検索するテスト2
    Chromeを起動する
    Input Text  name=q  ${検索キーワード}
    Press Key  name=q  \\13
    # 結果出力と検証
    ${links} =  Get Webelements  css=h3 > a
    :For  ${link}  In  @{links}
        \  ${text} =  Get Text  ${link}
        \  Log To Console  ${text}
    Length Should Be  ${links}  10
    Close Browser

 
以上が、ひと通りの流れとなります。

 

その他資料

BuitInライブラリやSeleniumLibraryのその他の書き方については、以下にも書きました。

 

ソースコード

GitHubに上げました。 selenium_getting_started ディレクトリの中が、今回のファイルです。
https://github.com/thinkAmi-sandbox/RobotFramework-sample

Python3で動く、Robot Framework の SeleniumLibrary 3.0 リリースについて

この記事は「Robot Framework Advent Calendar 2017 - Qiita」の2日目の記事です。

 
今回は、Robot FrameworkでSeleniumを扱うライブラリ SeleniumLibrary の3.0のリリースについてです。

2017/12/10追記

SeleniumLibrary 3.0.0にはバグがあったようで、現在は 3.0.1 がリリースされています。
https://groups.google.com/forum/#!topic/robotframework-users/qsZjh__Tq4k

2017/12/10追記 ここまで

目次

 

今までのSelenium2Library 1.8.0について

Robot FrameworkからSeleniumを使う時、今までは Selenium2Library のリリース版である 1.8.0 を使ってきました。

そのため、テストコードで

*** Settings ***
Library  Selenium2Library

と、Selenium2Libraryを参照していました。

 
ただ、Selenium2Library 1.8.0 はPython2では動作するものの、Python3系に対応していません。そのため、Python3の環境にインストールしようとするとエラーになっていました。

# バージョン確認
(env_sl2_18) $ python --version
Python 3.6.3

# pipでインストール
(env_sl2_18) $ pip install robotframework-selenium2library
Collecting robotframework-selenium2library
  Using cached robotframework-selenium2library-1.8.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/private/var/folders/h0/l5plp4zd3517r988jpm481g00000gn/T/pip-build-tstkfayi/robotframework-selenium2library/setup.py", line 7, in <module>
        from ez_setup import use_setuptools
      File "/private/var/folders/h0/l5plp4zd3517r988jpm481g00000gn/T/pip-build-tstkfayi/robotframework-selenium2library/src/ez_setup.py", line 106
        except pkg_resources.VersionConflict, e:
                                            ^
    SyntaxError: invalid syntax
    
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/h0/l5plp4zd3517r988jpm481g00000gn/T/pip-build-tstkfayi/robotframework-selenium2library/

 

SeleniumLibrary 3.0 について

とはいえ、Python3系に対応するSeleniumLibraryの開発も行われていました。そして、2017/12/1に3.0.0がリリースされました。

 
3.0.0のリリースでの変更点は以下の通りです。

 
なお、SeleniumLibrary は昔も存在していたようですが、そのあたりの話は以下にまとまっています。
https://github.com/robotframework/SeleniumLibrary#history

 

過去のテストコードの扱いについて

気になるのが、過去のテストコードの扱いです。

そこで、SeleniumLibraryとSelenium2Libraryを参照しているテストコードをそれぞれ用意して試してみます。

selenium_library.robot

こちらはSeleniumLibraryを参照しています。

*** Settings ***
Library  SeleniumLibrary

*** Test Cases ***
ChromeでGoogleを開く
    Open Browser  https://google.co.jp  Chrome

 
selenium2_library.robot

こちらはSelenium2Libraryを参照しています。

*** Settings ***
Library  Selenium2Library

*** Test Cases ***
ChromeでGoogleを開く
    Open Browser  https://google.co.jp  Chrome

 

SeleniumLibrary 3.0での実行結果

SeleniumLibrary 3.0.0で両方のテストコードを流してみます。

(env_sl) $ python --version
Python 3.6.3

# SeleniumLibrary 3.0.0のインストール
(env_sl) $ pip install robotframework-seleniumlibrary
Collecting robotframework-seleniumlibrary
  Using cached robotframework_seleniumlibrary-3.0.0-py2.py3-none-any.whl
Collecting selenium>=2.53.6 (from robotframework-seleniumlibrary)
  Using cached selenium-3.8.0-py2.py3-none-any.whl
Collecting robotframework>=2.8.7 (from robotframework-seleniumlibrary)
  Using cached robotframework-3.0.2.tar.gz
Installing collected packages: selenium, robotframework, robotframework-seleniumlibrary
  Running setup.py install for robotframework ... done
Successfully installed robotframework-3.0.2 robotframework-seleniumlibrary-3.0.0 selenium-3.8.0

# selenium_library.robot を実行 => 成功
(env_sl) $ robot selenium_library.robot 
================
Selenium Library                                  
================
ChromeでGoogleを開く   | PASS |
----------------------
Selenium Library     | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
================

# selenium_library.robot を実行 => エラー
$ robot selenium2_library.robot 
[ ERROR ] Error in file '/path/to/selenium2_library.robot': Importing test library 'Selenium2Library' failed: ModuleNotFoundError: No module named 'Selenium2Library'
Traceback (most recent call last):
  None
PYTHONPATH:
...
================
Selenium2 Library                                                             
================
ChromeでGoogleを開く  | FAIL |
No keyword with name 'Open Browser' found.
----------------------
Selenium2 Library     | FAIL |
1 critical test, 0 passed, 1 failed
1 test total, 0 passed, 1 failed
================

Selenium2Libraryを参照している方がエラーとなり、過去のテストが通らなくなりました。

 

Selenium2Library 3.0 での実行結果

Selenium2Library時代のテストコードを直さないといけないのかと思いましたが、READMEを読むと、

Those migrating from Selenium2Library can install SeleniumLibrary so that it is exposed also as Selenium2Library:

pip install --pre --upgrade robotframework-selenium2library

The above command installs the normal SeleniumLibrary as well as a new Selenium2Library version that is just a thin wrapper to SeleniumLibrary. That allows importing Selenium2Library in tests while migrating to SeleniumLibrary.

とありました。

 
そこで、Selenium2Library 3.0をインストールし、試してみます。

# SeleniumLibraryの仮想環境を抜ける
(env_sl) $ deactivate

# 仮想環境を作って有効化
$ python -m venv env_sl2_30
$ source env_sl2_30/bin/activate
(env_sl2_30) $ python --version
Python 3.6.3

# インストールする
(env_sl2_30) $ pip install --upgrade --pre robotframework-selenium2library
Collecting robotframework-selenium2library
  Downloading robotframework-selenium2library-3.0.0b1.tar.gz (68kB)
    100% |████████████████████████████████| 71kB 456kB/s 
Collecting selenium>=2.53.6 (from robotframework-selenium2library)
  Using cached selenium-3.8.0-py2.py3-none-any.whl
Collecting robotframework>=2.8.7 (from robotframework-selenium2library)
  Using cached robotframework-3.0.2.tar.gz
Collecting robotframework-seleniumlibrary>=3.0.0a1 (from robotframework-selenium2library)
  Using cached robotframework_seleniumlibrary-3.0.0-py2.py3-none-any.whl
Installing collected packages: selenium, robotframework, robotframework-seleniumlibrary, robotframework-selenium2library
  Running setup.py install for robotframework ... done
  Running setup.py install for robotframework-selenium2library ... done
Successfully installed robotframework-3.0.2 robotframework-selenium2library-3.0.0b1 robotframework-seleniumlibrary-3.0.0 selenium-3.8.0

# SeleniumLibraryを参照しているテストコードを実行するとパスする
(env_sl2_30) $ robot selenium_library.robot 
================
Selenium Library                                               
================
ChromeでGoogleを開く    | PASS |
----------------------
Selenium Library     | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
================

# Selenium2Libraryを参照しているテストコードを実行してもパスする
(env_sl2_30) $ robot selenium2_library.robot 
================
Selenium2 Library                                  
================
ChromeでGoogleを開く    | PASS |
----------------------
Selenium2 Library     | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
================

両方のテストがパスしました。

 
内容をみると、pip installにて

Successfully installed robotframework-3.0.2 robotframework-selenium2library-3.0.0b1 robotframework-seleniumlibrary-3.0.0 selenium-3.8.0

のように、SeleniumLibraryとSelenium2Libraryの両方がインストールされているようです。

これにより、Selenium2Libraryを参照している過去のテストコードがあったとしても、Python3系のSeleniumLibraryでテストできます。

 
とはいえ、Selenium2LibraryはSeleniumLibraryのラッパーであり、バージョンをよく見ると robotframework-selenium2library-3.0.0b1 とベータ版のようです。

そのため、新しいテストコードではSeleniumLibraryを参照するほうが良いでしょう。

#nseg 第7回「オブジェクト指向設計実践ガイド」読書会に参加しました

11/29にギークラボ長野で開催された、NSEGの「オブジェクト指向設計実践ガイド」読書会の第7回に参加しました。

ここ数回参加できていなかったので、今回参加できて良かったです。

nseg.connpass.com

今回は第7章を読みました。今回はモジュールの話でした。

第5,6章に関連するところもあり、その章について教えてもらいながら読み進めました。

今回Rubyに詳しい方がいらっしゃらなかったため、

  • method_missing
  • extend
  • 特異クラス

については、また後日識者の方にお尋ねしたいと思いました。

なお、週末には「ながのRubyの会 #1」が開催されます。 naruby.connpass.com

 
ところで、Rubyのモジュールで思い出したのは、PythonのMixinでした。

このページにもある通り、Djangoとかで見かけます。
Django Class-Based-View Inspector -- Classy CBV

 
たとえば、

mixin_example.py

class FooMixin:
    def hello(self):
        print('hello Foo')

    def call(self):
        print('call Foo')

class BarMixin:
    def hello(self):
        print('hello Bar')

    def call(self):
        print('call Bar')

class Baz(FooMixin, BarMixin):
    def hello(self):
        print('hello Baz')


if __name__ == '__main__':
    baz = Baz()
    baz.hello()
    baz.call()

と、

  • Mixinが2つ
  • Bazクラスは両方のMixinクラスを継承

というソースコードがあるとします。

 
この場合、実行してみると、

$ python mixin_example.py 
hello Baz
call Foo

のように、

  • クラスにメソッドがあれば、そのクラスのものを使う
  • Mixinしたクラスの両方にメソッドがあれば、Mixinの順番(左が優先)で使われる

となります*1

このあたりの話は以下が詳しく、参考になりました。
The History of Python.jp: メソッド解決順序(MRO)

*1:読書会の時はうろ覚えですみませんでした

Python + tweepyにて、Video URLを取得する

TwitterのVideo URLの取得方法を調べる機会がありましたので、メモとして残します。

なお、Twitter用ライブラリは、 tweepy を使います。

 
目次

 

環境

 

tweepyでTwitter APIを操作する

各種トークンを取得します。

auth = tweepy.OAuthHandler(CONSUMER_TOKEN, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
api = tweepy.API(auth)

 
tweepyにはCursorオブジェクトがあります。これを使うと、指定した数のツイートを取得できるため、ページングなどを気にする必要がないです。
Cursor Tutorial — tweepy 3.6.0 documentation

for status in tweepy.Cursor(api.user_timeline, 
    id='Twitter', since_id=560070183650213887,
    tweet_mode='extended').items(34):
        i += 1
print(i)

 
実行すると、

34

が出力されます。

 

ツイートとVideo URLが140文字に収まる場合

Video URLはどこに含まれているかを調べたところ、 extended_entities にあるようです。
Twitter REST APIを使って30秒動画の取得。$tweet->extended_entities->media[0]->video_info->variants[0]->url – エコテキブログ

以下のツイートで試してみます。
Twitterさんのツイート: "You can now shoot, edit and share video on Twitter. Capture life's most moving moments from your perspective. http://t.co/31JoMS50ha"

TWEET_ID = 560070183650213889
status = api.get_status(TWEET_ID)
print(status.text)
if hasattr(status, 'extended_entities'):
    for media in status.extended_entities.get('media', [{}]):
        if media.get('type', None) == 'video':
            print('video url: ' + media['video_info']['variants'][0]['url'])

実行結果です。

You can now shoot, edit and share video on Twitter. Capture life's most moving moments from your perspective. http://t.co/31JoMS50ha
video url: https://video.twimg.com/ext_tw_video/560070131976392705/pu/vid/640x360/vmLr5JlVs2kBLrXS.mp4

 

ツイートとVideo URLが140文字を超える場合

ツイートとVideo URLが140文字を超える場合は、少し動作が変わります。

別のツイート*1で実行してみると、

<長いツイート>やまな… https://t.co/xxx

みたいに、「…」でツイートが省略された上、extended_entitiesを取得できません。

 
この場合はどうするか調べたところ、APIを呼ぶ時に tweet_mode='extended' をつければ良さそうです。
Twitter Amplify Videos do not contain extended_entities · Issue #731 · tweepy/tweepy

 
また、 tweet_mode='extended'とした時は、 status.text がなくなり、 status.full_text になります。

# status = api.get_status(TWEET_ID)
# print(status.text)
status = api.get_status(TWEET_ID, tweet_mode='extended')
print(status.full_text)

 
実行してみます。

<長いツイート>やまない https://t.co/yyyy
video url: https://video.twimg.com/ext_tw_video/xxx.mp4

取得できました。また、短縮URLも先ほどのとは異なっていました。

 

【未解決】Videoが30秒より長い場合

こんな感じのツイートの場合、どう取得すればよいか不明です...
Twitter Videoさんのツイート: "Now, everyone can post videos up to 140 seconds long! We can’t wait to see the amazing videos you create and share. https://t.co/DFsuvnXkuL"

 

ソースコード

GitHubに上げました。 e.g._video_url ディレクトリの中身が今回のファイルです。
thinkAmi-sandbox/tweepy-sample thinkAmi-sandbox/tweepy-sample

*1:依頼された方のプライベートな感じなので、伏せ字にしておきます