Robot Framework + FtpLibraryで、FTPサーバと通信する

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

今回はRobot Frameworkを使ってFTPサーバと通信してみます。

 
目次

 

環境

  • Python 3.6.3
  • Robot Framework 3.0.2
  • FtpLibrary 1.4
    • 後述の通り、現時点のリリース版ではPython3系に対応していないため、GitHubからインストール
  • FTPサーバ
    • pyftpdlib 1.5.3

 

FTPサーバの用意

pyftpdlibによる実装

今回は、PythonFTPサーバである pyftpdlib を使います。
giampaolo/pyftpdlib: Extremely fast and scalable Python FTP server library

 
GitHubにあるREADMEに従い、実装します。

FTPサーバの設定は

とします。

import pyftpdlib.authorizers
import pyftpdlib.handlers
import pyftpdlib.servers
import pathlib
import shutil


def run_server():
    # FTPサーバのルートディレクトリを作る
    # __file__のパスを絶対パスに変換し、ルートディレクトリのパスも追加する
    ftp_server_root_path = pathlib.Path(__file__).resolve().parent.joinpath('serv')
    # ディレクトリがある場合は、一度削除しておく
    if ftp_server_root_path.exists():
        # pathlibのrmdir()ではファイルがあると削除できないので、shutilを使う
        shutil.rmtree(ftp_server_root_path.as_posix())
    # ディレクトリを作って、パーミッションは777にしておく
    ftp_server_root_path.mkdir()
    ftp_server_root_path.chmod(0o777)
    print(ftp_server_root_path)

    # 認証ユーザーを作る
    authorizer = pyftpdlib.authorizers.DummyAuthorizer()
    authorizer.add_user('user', 'password', ftp_server_root_path.as_posix(), perm='elradfmw')

    # 個々の接続を管理するハンドラを作る
    handler = pyftpdlib.handlers.FTPHandler
    handler.authorizer = authorizer

    # FTPサーバーを立ち上げる
    # Unix系だと、21番ポートはsuper userがbindできるポートなので、それ以外のポートにする
    # https://github.com/giampaolo/pyftpdlib/blob/master/docs/faqs.rst#why-do-i-get-socket-error-permission-denied-error-on-ftpd-starting
    server = pyftpdlib.servers.FTPServer(("127.0.0.1", 12345), handler)
    server.serve_forever()


if __name__ == '__main__':
    run_server()

 
ちなみに、ファイルパス操作は pathlib モジュールを使いました。Python3.4から使えます。

例えば、ディレクトリの絶対パスを取得する場合、

# os.pathを使う場合
import os
path_by_os = os.path.abspath(os.path.dirname(__file__))

# pathlibを使う場合
import pathlib
path_by_pathlib = pathlib.Path(__file__).resolve().parent

となります。個人的には、pathlibの方が見やすいと感じました。

 

pyftpdlibの起動

上記スクリプトftp_server.py として保存し、起動します。

$ python ftp_server.py 
/path/to/ftp_library_sample/serv
[I 2017-12-14 05:37:03] >>> starting FTP server on 127.0.0.1:12345, pid=34184 <<<
[I 2017-12-14 05:37:03] concurrency model: async
[I 2017-12-14 05:37:03] masquerade (NAT) address: None
[I 2017-12-14 05:37:03] passive ports: None

 

Robot FrameworkのFTPLibraryを使う

インストール

Robot FrameworkでFTPサーバと通信するためには、 Robot-Framework-FTP-Library を使います。
https://github.com/kowalpy/Robot-Framework-FTP-Library

 
このライブラリはpipでインストールできます。

ただ、2017/12/14現在、PyPIにあるものはPython3系に対応していません*1

そのため、GitHubからインストールします。
Please support python3+ · Issue #10 · kowalpy/Robot-Framework-FTP-Library

$ pip install git+https://github.com/kowalpy/Robot-Framework-FTP-Library

 

Robot Frameworkのテストケースを実装

ドキュメントに従って実装します。
https://kowalpy.github.io/Robot-Framework-FTP-Library/FtpLibrary.html

今回は、

  • FTPサーバへの接続
  • FTPサーバへのアップロード
  • FTPサーバからのダウンロード

を実装してみます。

*** Settings ***
Library  FtpLibrary


*** Variables ***
${USER}  user
${PASSWORD}  password


*** TestCases ***
FTPサーバに接続する
    ftp connect  127.0.0.1  ${USER}  ${PASSWORD}  12345
    Mkd  foo
    # Welcome Messageを表示
    ${welcome} =  Get Welcome
    # 改行して見やすくする
    Log To Console  ${\n} ${welcome}
    # カレントディレクトリを表示
    ${path} =  Dir
    Log To Console  ${\n} ${path}
    FTP CLOSE

FTPサーバにファイルをアップロードする
    ftp connect  127.0.0.1  ${USER}  ${PASSWORD}  12345
    Mkd  upload
    Cwd  upload
    Upload File  result_google_python.png  uploaded.png
    FTP CLOSE

FTPサーバからファイルをダウンロードする
    ftp connect  127.0.0.1  ${USER}  ${PASSWORD}  12345
    # uploadディレクトリとupload.pngは前のテストで作成済なので、それを利用する
    Cwd  upload
    Download File  uploaded.png  downloaded.png
    FTP CLOSE

 

テストの実行

実行前のディレクトリの様子です。

f:id:thinkAmi:20171214210533p:plain:w250

 
テストを実行します。問題なく終了しました。

$ robot test_ftp.robot 
=========================
Test Ftp                 
=========================
FTPサーバに接続する      ...
 220 pyftpdlib 1.5.3 ready.
..
 ['drwxr-xr-x   2 you group  68 Dec 14 06:00 foo']
FTPサーバに接続する    | PASS |
-------------------------
FTPサーバにファイルをアップロードする    | PASS |
-------------------------
FTPサーバからファイルをダウンロードする    | PASS |
-------------------------
Test Ftp    | PASS |
3 critical tests, 3 passed, 0 failed
3 tests total, 3 passed, 0 failed
=========================

 
実行後のディレクトリの様子です。

アップロードとダウンロードがうまくいっているようです。

f:id:thinkAmi:20171214210601p:plain:w250

 
FTPサーバ側にもログが残っていました。

[I 2017-12-14 06:00:00] >>> starting FTP server on 127.0.0.1:12345, pid=34184 <<<
[I 2017-12-14 06:00:00] concurrency model: async
[I 2017-12-14 06:00:00] masquerade (NAT) address: None
[I 2017-12-14 06:00:00] passive ports: None
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[] FTP session opened (connect)
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] USER 'user' logged in.
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] MKD /path/to/ftp_library_sample/serv/foo 257
[I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] FTP session closed (disconnect).
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[] FTP session opened (connect)
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] USER 'user' logged in.
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] MKD /path/to/ftp_library_sample/serv/upload 257
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] CWD /path/to/ftp_library_sample/serv/upload 250
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] STOR /path/to/ftp_library_sample/serv/upload/uploaded.png completed=1 bytes=213754 seconds=0.014
[I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] FTP session closed (disconnect).
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[] FTP session opened (connect)
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] USER 'user' logged in.
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] CWD /path/to/ftp_library_sample/serv/upload 250
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] RETR /path/to/ftp_library_sample/serv/upload/uploaded.png completed=1 bytes=213754 seconds=0.004
[I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] FTP session closed (disconnect).

 

ソースコード

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

*1:pip installできますが、参照して実行すると「ailed: SyntaxError: Missing parentheses in call to 'print'. Did you mean print(int "Starting Robot Framework Ftp Library as a remote server ...")? (FtpLibrary.py, line 451) 」が発生します

Robot Framework向けのIDE、RED(Robot Editor)について

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

前回、エディタやIDEのRobot Frameworkプラグインを試してみました。
エディタやIDEのRobot Frameworkプラグインについて - メモ的な思考的な

 
Robot Framework専用のエディタがないかを調べたところ、

がありました。

ただ、RIDEはPython3.x系では動作しないとのことでした*1

Notice that similarly as Robot Framework, RIDE does not yet support Python 3. Notice also that on OS X RIDE requires 32-bit Python version.

https://github.com/robotframework/RIDE/wiki/Installation-Instructions

 
手元の環境がPython3.x系であるため、今回は RED (Robot Editor) を試してみました。

 
目次

 

環境

 
今回はMacにインストールしましたが、WindowsLinuxでも動作するようです。

 

REDのインストール

インストール方法は以下に記載がありました。

 
今回はREDを試すだけなので、一番お手軽なGitHubのzipを利用します。

REDのreleaseページより、環境に合わせて最新版をダウンロードします。
Releases · nokia/RED

Macであれば、 RED_0.8.1.20171123105248-macosx.cocoa.x86_64.zip をダウンロードします。

このzipには、Eclipse + RDE が含まれています。そのため、別途Eclipseをインストールする必要はありません。

もしEclipseが手元にある場合は、RDEだけをインストールすれば良いようです。

 
ダウンロードしたzipを展開した後に開くと、 開発元が未確認のため開けません。 というエラーメッセージが表示されました。

そのため、以下を参考にしてREDを実行しました。
macOS Sierra: 開発元が未確認のアプリケーションを開く

 

プロジェクトの作成

File > New > Robot Project を選択します。project nameは任意の名前をつけます。

 

Robot Frameworkのインストール

REDのワークスペースの中でRobot Frameworkをインストールすると、自動的に認識されます。

デフォルトのワークスペースは以下の通りです。

/path/to/rf_red/Eclipse.app/Contents/MacOS/workspace

 
ターミナルを使って、デフォルトのワークスペースの中で、Robot Frameworkとそのライブラリのインストールを行います。

# Python3の仮想環境を作成
$ python -m venv rfenv363

# 仮想環境を有効化
$ source rfenv363/bin/activate

# とりあえずRobot FrameworkとSeleniumLibraryを入れる
(rfenv363) $ pip install robotframework-selenium
...
Successfully installed robotframework-3.0.2 robotframework-seleniumlibrary-3.0.1 selenium-3.8.0

 
仮想環境のRobot Frameworkが認識されているかを、 環境設定 > Robot Framework > Install Framework にて確認します。ワークスペースの中であれば、自動的に認識されているはずです。

もし存在しなければ、先ほど作成した仮想環境の bin ディレクトリを指定します。

 

補完設定

設定内容

以下を参考に、デフォルトから補完設定を修正します。
Eclipseの補完設定をカスタマイズして爆速コーディング - ser1zw's blog

REDのRobot Framework設定はこんな感じでした。

f:id:thinkAmi:20171211214716p:plain

ただ、Robot Frameworkでは日本語のテストケース名も書けるのですが、自動有効化トリガーとして設定するのが難しいです*2

そのため、 Ctrl + Space というショートカットも併用します。

また、専用IDEだけあり、SeleniumLibrary 3系のキーワードも問題なく補完してくれます。

 

コンテンツ・アシストの様子

日本語のみの場合は、 Ctrl + Space にてこんな感じで補完されます。

f:id:thinkAmi:20171211214416p:plain

 
プレフィックスとして英語を付けていると、自動補完されます。見た目は日本語のみと同じですね。

f:id:thinkAmi:20171211214503p:plain

 
SeleniumLibrary3.x系も自動補完されます。

f:id:thinkAmi:20171211214546p:plain

 

Robot Frameworkのテストケースを作成

IDEの準備ができたため、Robot Frameworkのテストケースを作成します。

上記で作成したmyprojectを右クリック > New > Robot Test Suite にてテストケースファイルを作成します。

f:id:thinkAmi:20171211214750p:plain

 
今回は、こんな感じのテストケースファイルを作成しました。

*** Settings ***
Library    SeleniumLibrary

*** Keywords ***
はろー
    Log To Console    ワールド
        
myはろー
    Log To Console    こんにちわ

*** Test Cases ***
テスト
    はろー
    myはろー
    Log To Console    hello

 

テストの実行

REDのコンソールよりテストを実行してみます。

=============
Myproject
=============
Myproject.Mytest
=============
???    ????
?????
hello
| PASS |
------------------
Myproject.Mytest    | PASS |

テストはパスしたものの、コンソールに出力した日本語が文字化けしてしまいました。

 
Eclipseの事例では、eclipse.ini に追記すると文字化けが解消されるとの記載がありました。
Eclipseのコンソール文字化け解決法。 - Qiita

同じようなファイルを探してみたところ、それっぽいのが以下にありました。

/rf_red/Eclipse.app/Contents/Eclipse/RED.ini

 
ただ、11行目のように記載してREDを再起動しましたが、結果は同じく文字化けしたままでした。

1 -startup
2 ../Eclipse/plugins/org.eclipse.equinox.launcher_1.4.0.v20161219-1356.jar
3 --launcher.library
4 ../Eclipse/plugins/org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.550.v20170928-1359
5 --launcher.appendVmargs
6 -vmargs
7 -Xms512m
8 -Xmx2g
9 -XstartOnFirstThread
10 -Dorg.eclipse.swt.internal.carbon.smallFonts
11 -Dfile.encoding=UTF-8

 
最後のコンソール部分は文字化けしたままですが、Macのターミナルから実行すると問題ないので気にしないことにしました。

 
以上、Robot Framework専用のIDEのREDを使ってみました。Nokiaで使われていることもあり、十分実用的なものでした。

 
ちなみに、メーリングリストには

To create RED we needed a valid business reason to do so. There is a big group of Eclipse users in Nokia who would take advantage in having integrated dev&testing environment. IntelliJ is also considered but we still have a lot to do in RED.

という投稿もありました。IntelliJにも来るといいなー。
https://groups.google.com/d/msg/robotframework-users/c8tNdBVL5WE/kT-uQDROEQAJ

*1:なお、fork先ではexperimental support としてPython3.xで動くようです https://github.com/robotframework/RIDE/issues/1719

*2:プレフィックスとして「test_」などを付けておけば、自動補完は動作しますが...

エディタやIDEのRobot Frameworkプラグインについて

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

 
Robot Frameworkのテストケースはプレーンテキスト(.robot.txt)で書けるため、任意のエディタやIDEを使えます。

ただ、効率的にテストケースを書くためには、

  • シンタックスハイライト
  • キーワード補完
  • キーワードや変数の定義へジャンプ

などが欲しいです。

Robot Frameworkの公式サイトの Tools > Editor では、エディタやIDEプラグインについて紹介されています。
http://robotframework.org/#tools

そこで今回は、その中から

を試してみました。

 
目次

 

環境

 
ちなみに、自分は普段JetBrains系のIDEを使っているため、プラグインintellibot を使っています。

 

注意:SeleniumLibraryの補完について

2017/12/12現在、SeleniumをRobot Frameworkから扱う SeleniumLibrary の最新版(3.0.1)を参照した場合、JetBrains系のIDEVisual Studio Codeのプラグインではキーワード補完がうまく動作しないです*1

Selenium2Library 1.8.0 であればキーワード補完ができるようです。が、Python3.x系では動作しないです...

 

JetBrains系IDEプラグイン

3つのプラグインがありました。

このうち、 Robot PluginJava向けらしく、今回は試しませんでした。

 

Robot Framework Support (robot-plugin)

手元の環境で試したところ、

ができました。

一方、

がありました。

 

IntelliBot

手元の環境で試したところ、

  • シンタックスハイライト
  • キーワード補完
    • 標準ライブラリや、ユーザ定義のキーワード(日本語のキーワードもOK)
  • 定義へのジャンプ

などができました。

一方、

という問題がありました。

とはいえ、日本語のキーワード補完もできるので、こちらを使っています。

 
また、intelliBotにはオプション設定がいくつかあります。自分の場合はデフォルト設定に加え、Robot Frameworkのサンプルの書式に合わせ、 Capitalize Keywords にチェックを入れています。

これにより、 should be equalShould Be Equal として補完されます。少々挙動が怪しいですが、許容範囲内です。

 

Visual Studio Codeのプラグイン

現在は4つほど見つかりました。

Robot Framework Intellisense

手元の環境では

ができました。

一方、

  • 実装されているはずのコード補完やコードジャンプが効かない

がありました。

その他の既知の問題については、以下に記載がありました。
https://github.com/tomi/vscode-rf-language-server/blob/master/client/KNOWNISSUES.md

 

robotframework

手元の環境では

ができました。

コード補完やコードジャンプの機能は元々無いようです。

 

robot framework language

手元の環境では

ができました。

 

RobotF Extension

手元の環境では

  • 英語のキーワードを補完
  • 英語のキーワードの定義へ移動
  • 英語のキーワード名を置換

などができました。

一方、日本語については上記の機能が実行できませんでした。

また、シンタックスハイライトについては機能は実装されていないようです。説明にも、

You need to have robotframework language support for visual studio code if you want text highlighter

とありました。

 

Atomプラグイン

Atomプラグインを見たところ、階層的な依存が見られたため、すべてを入れてみます。

なお、インストール方法はこちらを参考にしました。
https://gist.github.com/Tset-Noitamotua/22e9edaade89e52fb10fcefd51a4547b

$ apm install language-robot-framework
$ apm install autocomplete-robot-framework
$ apm install hyperclick
$ apm install hyperclick-robot-framework

 
試してみたところ、

ができました。

一方、

  • 日本語のコード補完
  • キーワード定義へのジャンプ

ができませんでした。

 

その他

以下にも情報がありました。

 
ここで紹介した以外にも、エディタやIDEのRobot Frameworkのプラグインがあるため、いろいろと試してみてはいかがでしょうか。

シンタックスハイライトがあるだけでも分かりやすくなります。

*1:後述の通り、Atomプラグインでは動作しました

Robot Frameworkの変数ファイルについて

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

 
前回、Robot Frameworkにおける変数定義と優先順について書きました。

そこでは 変数ファイル については省略していたため、今回は変数ファイルについて記載します。

 
目次

 

環境

  • Robot Framework 3.0.2

 

変数ファイルとは

公式ドキュメントではこちらに記載があります。

 
Robot Frameworkでは、Pythonで書いた

をRobot Frameworkの変数として扱えます。それらが書かれたファイルのことを変数ファイルと呼びます。

Pythonで書けるので、リソースファイルと比べて柔軟な定義ができます。

 
今回はユーザガイドに従い、いろいろと試してみます。

 

変数ファイルをテストケースファイルで読み込むには

テストケースファイルのSettingsで Variables を使い変数ファイルを指定します。

以下の例では、テストケースファイルと同じディレクトリにある variable_file.py を変数ファイルとして読み込み、Robot Frameworkの変数として追加します。

*** Settings ***
Variables    variable_file.py

 

いろいろな変数ファイル中の定義

変数ファイルではいくつかの方法でRobot Framework用変数を定義できます。

 

Pythonの変数(定数)

Pythonファイルに

FOO = 'foo'

Pythonの変数(定数)を定義すると、Robot Frameworkの変数となります。

テストケースを

*** Test Cases ***
Pythonの定数
    log to console  ${FOO}

として実行すると、

Pythonの定数    foo

と出力されます。

 

Pythonの辞書

Pythonの辞書も同様です。

BAR = {'ham': 'spam'}

と変数ファイルを定義します。

テストケースを

Pythonの辞書
    log to console  ${BAR}

として実行した場合、

Pythonの辞書    {'ham': 'spam'}

Robot Frameworkの辞書型の変数となります。

 

Pythonの辞書で、定数名にプレフィックスを付けた場合

Pythonの辞書型変数名にプレフィックスを付けると、明示的に辞書型の定数として定義できます。

プレフィックス__変数名 という形であり、Robot Frameworkではプレフィックスが取れた変数名として認識されます。

以下の例の場合、Robot Frameworkでは BAZ として認識されます。

DICT__BAZ = {'egg': 'spam'}

 
また、プレフィックスを付けると、Robot Frameworkで . を使ったアクセスができます。

プレフィックスの有無での挙動を比較するため、それぞれの辞書を用意します。

BAR = {'ham': 'spam'}
DICT__BAZ = {'egg': 'spam'}

テストケースを

Pythonの辞書でプレフィックス無しの場合keyアクセスができない
    log to console  ${BAR.ham}

Pythonの辞書でプレフィックス付の場合keyアクセスができる
    log to console  ${BAZ.egg}

として実行した場合、

Pythonの辞書でプレフィックス付    {'egg': 'spam'}
Pythonの辞書でプレフィックス付    | PASS |
------------------------------------------------------------------------------
Pythonの辞書でプレフィックス無しの場合keyアクセスができない    | FAIL |
Resolving variable '${BAR.ham}' failed: AttributeError: 'dict' object has no attribute 'ham'

となります。

 

プレフィックスがないと変数名が重複する場合

Pythonではプレフィックスを付けた変数と付けない変数は別モノとなるため、重複していることに気づかないかもしれません。

例えば、Pythonコード中に以下を書いていたとしてます。

DICT__DUPLICATE = {'dup': 1}
DUPLICATE = {'dup': 2}

テストケースを

変数が重複していた場合
    log to console  ${DUPLICATE}

として実行した場合

変数が重複していた場合    {'dup': 1}

となります。

最初に定義されていたPythonの変数がRobot Frameworkの変数として使われます。

 

Pythonの関数の戻り値

Pythonの関数の戻り値をRobot Frameworkの変数として設定することもできます。

def get_result():
    return 1


RESULT = get_result()

テストケースで

Pythonの関数の結果を設定した変数
    log to console  ${RESULT}

として実行した場合

Pythonの関数の結果を設定した変数    1

となります。

Pythonの関数そのもの

Pythonの関数そのものも、Robot Frameworkの変数として定義できます。

def get_random():
    return random.randint(0, 20)

テストケースにて

Pythonの関数そのもの
    log to console  ${get_random}

として実行した場合、

Pythonの関数そのもの    <function get_result at 0x10650d400>

となります。

 

アンダースコアで始まるPythonの関数

関数名が _ から始まる場合、Robot Frameworkの変数としては使えません。

def _under_score_func():
    pass

テストケースにて

Pythonの関数でアンダースコア始まりのものは対象外
    log to console  ${_under_score_func}

として実行した場合、

Pythonの関数でアンダースコア始まりのものは対象外    FAIL |
Variable '${_under_score_func}' not found.

となります。

 

Pythonの定数や関数のうち、除外したいものについて

今まで見てきた通り、Pythonファイルに書かれたものはRobot Frameworkの変数として使用できます。

ただ、アンダースコア始まりの関数を除き、Pythonファイルで定義されているものはRobot Frameworkの変数となってしまいます。それでは不都合があるかもしれません。

その場合、 __all__ を使うことで、Robot Frameworkの変数として扱いたいものを絞り込めます。

例えば、

__all__ = ['include_func', 'INCLUDE_VARIABLE']

INCLUDE_VARIABLE = 'foo'
EXCLUDE_VARIABLE = 'bar'


def include_func():
    pass


def exclude_func():
    pass

とします。

テストケースにて

Pythonの定数で__all__にあるものは対象
    log to console  ${INCLUDE_VARIABLE}

Pythonの定数で__all__に無いものは対象外
    log to console  ${EXCLUDE_VARIABLE}

Pythonの関数で__all__にあるものは対象
    log to console  ${include_func}

Pythonの関数で__all__に無いものは対象外
    log to console  ${exclude_func}

として実行した場合、

Pythonの定数で__all__にあるものは対象    foo
Pythonの定数で__all__にあるものは対象    | PASS |
----------------------------------------
Pythonの定数で__all__に無いものは対象外    | FAIL |
Variable '${EXCLUDE_VARIABLE}' not found. Did you mean:
    ${INCLUDE_VARIABLE}
----------------------------------------
Pythonの関数で__all__にあるものは対象    <function include_func at 0x10650d510>
Pythonの関数で__all__にあるものは対象    | PASS |
----------------------------------------
Pythonの関数で__all__に無いものは対象外    | FAIL |
Variable '${exclude_func}' not found.

となります。

 

Pythonのクラス変数・インスタンス変数を使う

Pythonのクラス変数・インスタンス変数も使えます。

ただし、

  • クラスのインタンスは使用不可
  • Pythonのクラス変数・インスタンス変数を扱うには、クラス名とクラスを定義してあるファイル名の一致が必要

です。

例えば、Pythonファイルに2つのクラスを定義します。

variable_file_class.py

class variable_file_class:
    class_val = 'python'

    def __init__(self):
        self.instance_val = 'self val'


instance_from_class = variable_file_class()


class diff_variable_file:
    diff_class_val = 'diff class'

    def __init__(self):
        self.diff_instance_val = 'self diff val'

テストケースにて

Pythonのクラスをインスタンス化して変数に設定したものは参照できない
    log to console  ${instance_from_class}

Pythonクラスのクラス変数を使う
    log to console  ${class_val}

Pythonクラスのインスタンス変数を使う
    log to console  ${instance_val}

Pythonクラスはファイル名と同じでないとクラス変数を参照できない
    log to console  ${diff_class_val}

Pythonクラスはファイル名と同じでないとインスタンス変数を参照できない
    log to console  ${diff_instance_val}

として実行した場合、

Pythonのクラスをインスタンス化して変数に設定したものは参照できない    | FAIL |
Variable '${instance_from_class}' not found.
----------------------------------
Pythonクラスのクラス変数を使う    python
Pythonクラスのクラス変数を使う    | PASS |
----------------------------------
Pythonクラスのインスタンス変数を使う    self val
Pythonクラスのインスタンス変数を使う    | PASS |
----------------------------------
Pythonクラスはファイル名と同じでないとクラス変数を参照できない     | FAIL |
Variable '${diff_class_val}' not found.
----------------------------------
Pythonクラスはファイル名と同じでないとインスタンス変数を参照できない    | FAIL |
Variable '${diff_instance_val}' not found.

となります。

 

YAMLファイルからの読み込み

変数ファイルはYAMLファイルとしても用意できます。

なお、YAMLファイルを変数ファイルとして使う場合は、PyYAMLのインストールが必要です。

pip install pyyaml

変数ファイルをYAML

yaml_dict:
    one: 1
    two: 2

と記載し、

YAMLファイルから読み込む
    log to console  ${yaml_dict}

と使った場合です。

実行結果は

YAMLファイルから読み込む    {'one': 1, 'two': 2}

です。

 

特殊な関数 get_variables() について

Robot Frameworkの変数ファイルでは、特殊な関数 get_variables() が使えます。

例えば、変数ファイルにて

def get_variables():
    return {
        'SPECIAL_FUNC': {'hoge': 'fuga'}
    }

とします。

テストケースにて

get_variablesを使う
    log to console  ${SPECIAL_FUNC}

として実行した場合、

get_variablesを使う    {'hoge': 'fuga'}

となります。

 
なお、 get_variables() 関数自体を変数として使うことはできません。

テストケースにて

get_variables自体は取れない
    log to console  ${get_variables}

として実行した場合、

get_variables自体は取れない    | FAIL |
Variable '${get_variables}' not found.

となります。

 

気になる get_variables() の挙動について

発生条件などがわかっていないので気になったというメモ書きです。

get_variables() を使った変数ファイルと、それ以外の変数ファイルを同時に読み込んだ場合、get_variables() の変数以外が

Pythonの定数    | FAIL |
Variable '${FOO}' not found.

のようになったことがありました。

復活した原因もよくわかっていません。

そのため、もし同じような事象が発生した場合は、 get_variables() を含む変数ファイルを外してみると良いかもしれません。

 

ソースコード

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

Robot Frameworkにおける変数定義と優先順について

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

今回はRobot Frameworkで使う変数の定義方法と、変数名が重複した時の優先順についてです。

 
目次

 

環境

  • Robot Framework 3.0.2

 

Robot Frameworkの変数定義箇所

Robot Frameworkでは、いくつかの箇所で変数を定義できます。

ユーザーガイドでは以下に記載されています。

 
今回はそのうち、以下について記載します。

  • テストケースローカル
    • テストスイートやテストグローバルもあるが、今回は省略
  • 変数テーブル
  • リソースファイル
  • コマンドライン
  • 変数ファイル
    • 今回は省略

 

テストケースローカル

テストケースの中でだけ有効な変数です。

例えば、以下では変数 ${local} を定義し、値を設定しています。

ローカルで定義した変数
    Set Variable  ${local}  local_variable
    Log To Console  ${local}

実行結果です。

$ robot test_variable_priorities.robot 
...
ローカルで定義した変数    .local_variable

 

変数テーブル

テストファイル内の全テストケースで使える変数です。

以下のように定義します。

*** Variables ***
${VARIABLE_TABLE}  testcase_variable_table

 
テストケースでの使用例です。

変数テーブルで定義された変数
    Log To Console  ${VARIABLE_TABLE}

 
実行結果です。

$ robot test_variable_priorities.robot 
...
変数テーブルで定義された変数    testcase_variable_table

 

リソースファイル

リソースファイルという、外部ファイルにも変数を定義できます。

リソースファイルでは変数テーブルにて変数を定義します。

*** Variables ***
${RESOURCE}  resource

 
テストケースで利用するには、以下のように SettingsResource でリソースファイルパスを指定します。

*** Settings ***
Resource  variable_priorities_resource.robot

 
テストケースで

importしたリソースファイルで定義された変数
    Log To Console  ${RESOURCE}

とした場合の実行結果です。

$ robot test_variable_priorities.robot 
...
importしたリソースファイルで定義された変数    resource

 
なお、リソースファイルはネストすることができます。

variable_priorities_nest_resource.robot

*** Variables ***
${NEST_RESOURCE}  nest_resource_val

 
variable_priorities_resource.robot

こちらがリソースファイルを読み込んでいるリソースファイルです。

*** Settings ***
Resource  variable_priorities_nest_resource.robot

 
test_variable_priorities.robot

こちらが「リソースファイル読み込んでいるリソースファイル」を読み込んでいるテストケースです。

*** Settings ***
Resource  variable_priorities_resource.robot

*** Test Cases ***
ネストしたリソースファイルで定義された変数
    Log To Console  ${NEST_RESOURCE}

 
実行結果です。

$ robot test_variable_priorities.robot 
...
ネストしたリソースファイルで定義された変数    nest_resource_val

 

コマンドライン

コマンドラインからも、 -v オプションを使うことで変数の値を与えられます。

テストケースには、コマンドラインから渡される予定の変数名を定義しておきます。

*** Test Cases ***
コマンドラインで指定した変数
    Log To Console  ${FROM_COMMAND_LINE}

 
実行結果です。

$ robot -v FROM_COMMAND_LINE:from_command_line test_variable_priorities.robot
...
コマンドラインで指定した変数    from_command_line

 

変数の優先順

複数箇所で変数が定義されている場合、名前が重複する可能性があります。

重複した時の挙動を確認します。

 

リソースファイルとテストケースで同じ変数名を使う場合

リソースファイル

*** Variables ***
${RESOURCE}  resource

テストケース

*** Test Cases ***
リソースファイルのみの変数をこのテストケースだけで上書きした場合
    Set Test Variable  ${RESOURCE}  adohoc
    Log To Console  ${RESOURCE}

にて変数 ${RESOURCE} が重複した場合です。

実行結果は

$ robot test_variable_priorities.robot 
...
リソースファイルのみの変数をこのテストケースだけで上書きした場合    .adohoc

とテストケースの中の値が優先されます。

 

リソースのimport元と先で重複した変数

リソースファイルとネストしたリソースファイルで変数名が重複した場合です。

ネストしたリソースファイル

*** Variables ***
${RESOURCE_IMPORT}  nest_resource_import

リソースファイル

*** Settings ***
Resource  variable_priorities_nest_resource.robot

*** Variables ***
${RESOURCE_IMPORT}  resource_import

にて変数 ${RESOURCE_IMPORT} が重複していた場合です。

テストケースを

*** Test Cases ***
リソースファイルのimport元と先で重複した変数
    Log To Console  ${RESOURCE_IMPORT}

として実行すると

リソースファイルのimport元と先で重複した変数    resource_import

と、最初に読み込まれたリソースファイルの変数の値となります。

 

リソースファイルとテストケースの変数テーブルで重複した変数

リソースファイル

*** Variables ***
${VARIABLE_TABLE}  resource_variable_table

テストケースの変数テーブル

*** Variables ***
${VARIABLE_TABLE}  testcase_variable_table

*** Test Cases ***
変数テーブルとリソースファイルで重複した変数
    Log To Console  ${VARIABLE_TABLE}

で変数 ${VARIABLE_TABLE} が重複していた場合です。

実行結果は

変数テーブルとリソースファイルで重複した変数    testcase_variable_table

と、テストケースの変数テーブルが優先されます。

 

リソースファイルとテストケースの変数テーブルとコマンドラインで重複した変数

リソースファイル

*** Variables ***
${COMMAND}  resource_command

テストケースファイルの変数テーブル

*** Settings ***
Resource  variable_priorities_resource.robot

*** Variables ***
${COMMAND}  testcase_command


*** Test Cases ***
コマンドラインと変数テーブルとリソースファイルで重複した変数
    Log To Console  ${COMMAND}

コマンドライン

$ robot -v COMMAND:command_line test_variable_priorities.robot 

で変数 ${COMMAND} が重複していた場合です。

実行結果は

$ robot -v COMMAND:command_line test_variable_priorities.robot 
...
コマンドラインと変数テーブルとリソースファイルで重複した変数    command_line

と、コマンドラインが優先されます。

 

ソースコード

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

Robot Frameworkを実行後に出力されるファイルについて

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

Robot Frameworkのデフォルト設定では、テストの実行後にログファイルやレポートファイルが自動生成されます。

ログファイル

f:id:thinkAmi:20171207225448p:plain

もしテストが失敗した場合、その詳細情報がログファイルに出力されます。

f:id:thinkAmi:20171207225541p:plain

 
レポートファイル(すべてのテストがパスした時)

f:id:thinkAmi:20171207225511p:plain

 
レポートファイル(いずれかのテストが失敗した時)

f:id:thinkAmi:20171207225527p:plain

 
また、自動生成されるファイル名のデフォルト名称は

  • log.html
    • ログファイル
  • report.html
    • レポートファイル
  • output.xml
    • ログXMLファイル
    • ログ、レポート等のファイルの元となるファイル

です。

デフォルト設定では、これらのファイルはテストを実行した時のカレントディレクトリに出力されます。

 
また、自動生成されるファイル以外にも、

  • xUnitと互換性のあるファイル
  • デバッグファイル

も追加で出力できます。

そこで今回は、それらのファイル出力を試してみます。

 
目次

 

環境

  • Python 3.6.3
  • Robot Framework 3.0.2

 

テストコード中でファイル名を変更 (不可)

レポートファイル名は、自動変数 ${REPORT FILE} に設定されています。

 
そこで、試しに ${REPORT FILE} を変更してみます。

*** Variables ***
# Variablesにてセット
${REPORT FILE}  rename_report_in_variables.html

*** TestCases ***

レポートファイル名を変更する
    # 見やすいように改行する
    Log To Console  ${EMPTY}

    # 変数としてセット
    Set Variable  ${REPORT FILE}  rename_report_in_test_cases1.html
    Log To Console  ${REPORT FILE}

    # グローバルな変数にセット
    Set Global Variable  ${REPORT FILE}  rename_report_in_test_cases2.html
    Log To Console  ${REPORT FILE}

実行結果です。

$ robot rename.robot 
==========================
Rename
==========================
レポートファイル名を変更する
../path/to/report.html
..rename_report_in_test_cases2.html
レポートファイル名を変更する    | PASS |
------------------------------------
Rename    | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
==========================
Output:  /path/to/output.xml
Log:     /path/to/log.html
Report:  /path/to/report.html

キーワード Set Global Variable では値の書き換えはできたものの、最終的なログファイル名は変わりませんでした。

 

コマンドラインオプションを使う

ファイル名はコマンドラインオプションから変更できます。

ヘルプに記載がありました。

$ robot --help
...
 -o --output file         XML output file. Given path, similarly as paths given
                          to --log, --report, --xunit, and --debugfile, is
                          relative to --outputdir unless given as an absolute
                          path. Other output files are created based on XML
                          output files after the test execution and XML outputs
                          can also be further processed with Rebot tool. Can be
                          disabled by giving a special value `NONE`. In this
                          case, also log and report are automatically disabled.
                          Default: output.xml
 -l --log file            HTML log file. Can be disabled by giving a special
                          value `NONE`. Default: log.html
                          Examples: `--log mylog.html`, `-l NONE`
 -r --report file         HTML report file. Can be disabled with `NONE`
                          similarly as --log. Default: report.html

 
試してみます。

$ robot --log re_log.html --report re_report.html --output re_output.xml rename.robot 
===============
Rename
===============
...
Output:  /path/to/re_output.xml
Log:     /path/to/re_log.html
Report:  /path/to/re_report.html

 
各ファイル名が変更されました。

 
ちなみに、Robot Frameworkのコマンドラインオプションは、いずれもテストファイルよりも前に付ける必要があります。

上記例の場合、ファイル rename.robot よりも前に書きます。書かない場合、

[ ERROR ] Parsing '--output' failed: Data source does not exist.

というエラーが出ます。

 

出力ファイルがいらない場合

なお、出力ファイルがいらない場合は、それぞれのコマンドラインオプションに None を設定します。

$ robot --log None --report None --output None rename.robot
...
Output:  None

 

xUnitのログと互換性のあるファイルが欲しい場合

コマンドラインオプション --xunit (-x) を使います。
http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#xunit-compatible-result-file

$ robot --log None --report None --output None --xunit xunit_report.xml rename.robot
...
Output:  None
XUnit:   /path/to/xunit_report.xml

結果です。

<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="Rename" tests="1" errors="0" failures="1" skip="0">
<testcase classname="Rename" name="レポートファイル名を変更する" time="0">
<failure message="AssertionError" type="AssertionError"></failure>
</testcase>
</testsuite>

 

デバッグファイルが欲しい場合

コマンドラインオプション --debugfile (-b) を使います。
http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#debug-file

$ robot --log None --report None --output None --debugfile debug.txt rename.robot
...
Debug:   /path/to/debug.txt
Output:  None

デバッグ内容がプレーンテキストとして作成されます。

中身はこんな感じです。

==============================================================================
20171207 23:14:38.725 - INFO - + START SUITE: Rename [ ]
==============================================================================
20171207 23:14:38.725 - INFO - +- START TEST: レポートファイル名を変更する [ ]
------------------------------------------------------------------------------
20171207 23:14:38.727 - INFO - +-- START KW: BuiltIn.Log To Console [ ${EMPTY} ]
20171207 23:14:38.727 - INFO - +-- END KW: BuiltIn.Log To Console (0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20171207 23:14:38.728 - INFO - +-- START KW: BuiltIn.Set Variable [ ${REPORT FILE} | rename_report_in_test_cases1.html ]
20171207 23:14:38.728 - INFO - +-- END KW: BuiltIn.Set Variable (1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20171207 23:14:38.728 - INFO - +-- START KW: BuiltIn.Log To Console [ ${REPORT FILE} ]
20171207 23:14:38.728 - INFO - +-- END KW: BuiltIn.Log To Console (0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20171207 23:14:38.729 - INFO - +-- START KW: BuiltIn.Set Global Variable [ ${REPORT FILE} | rename_report_in_test_cases2.html ]
20171207 23:14:38.729 - INFO - ${REPORT FILE} = rename_report_in_test_cases2.html
20171207 23:14:38.729 - INFO - +-- END KW: BuiltIn.Set Global Variable (1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20171207 23:14:38.729 - INFO - +-- START KW: BuiltIn.Log To Console [ ${REPORT FILE} ]
20171207 23:14:38.729 - INFO - +-- END KW: BuiltIn.Log To Console (0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20171207 23:14:38.730 - INFO - +-- START KW: BuiltIn.Fail [ ]
20171207 23:14:38.730 - FAIL - AssertionError
20171207 23:14:38.730 - DEBUG - Traceback (most recent call last):
  None
20171207 23:14:38.730 - INFO - +-- END KW: BuiltIn.Fail (0)
------------------------------------------------------------------------------
20171207 23:14:38.730 - INFO - +- END TEST: レポートファイル名を変更する (5)
------------------------------------------------------------------------------
20171207 23:14:38.731 - INFO - + END SUITE: Rename (43)
==============================================================================

 

ファイルにタイムスタンプが欲しい場合

コマンドラインオプション --timestampoutputs (-T) を使います。

$ robot --debugfile debug.txt --xunit xunit.xml --timestampoutputs rename.robot
...
Debug:   /path/to/debug-20171207-231818.txt
Output:  /path/to/output-20171207-231818.xml
XUnit:   /path/to/xunit-20171207-231818.xml
Log:     /path/to/log-20171207-231818.html
Report:  /path/to/report-20171207-231818.html

 
出力ファイルに関しては、他にも公式ドキュメントに書いてありますので、一度目を通しておくと良いかと思います。
3.5.1 Different output files - Robot Framework User Guide

Robot Frameworkのライブラリを自作する

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

Robot Frameworkを使っている中で、Robot Frameworkのライブラリを自作したいことがあるかもしれません。

ユーザーガイドにて方法を調べてみると、以下に記載がありました。
4.1 Creating test libraries - Robot Framework User Guide

 
そこで今回は、ユーザーガイドに従ってライブラリを自作してみます。

 
目次

 

環境

  • Robot Framework 3.0.2
  • Python 3.6.3

 

ライブラリの作成

ユーザーガイドによると、ライブラリはPythonJava、どちらでも作成できるようです。今回はPythonで作成してみます。

また、今回はターミナルにログを出力するライブラリを作ります。

 
Robot Frameworkの Programmatic logging APIs を使うことで、ターミナルにログが出力できるようです。
Programmatic logging APIs - Robot Framework User Guide

 
今回は、

  • logging APIのモジュール robot.api.logger を使用
  • Robot Frameworkのテストで使うためのキーワードは、英語と日本語の両方を用意
  • ライブラリ名は MyLibrary.py
    • PascalCaseで書く慣習のようですが、小文字だけでも問題ない
    • MyLibraryでもmy_libraryでも動作

とします。

MyLibrary.py

from robot.api import logger

def hello_world(key='default'):
    logger.console(f'{key} de hello world')

def ハロー_ワールド(key='デフォルト'):
    logger.console(f'{key} を使ったハローワールド')

 
完成したライブラリをRobot Frameworkで使うためには、PYTHONPATH など module search path と呼ばれる所にライブラリを置く必要があります。
3.4.5 Configuring where to search libraries and other extensions - Robot Framework User Guide

 
その場所に

  • 自作ライブラリファイルをそのまま
  • setuptoolsなどを使ってパッケージング

のいずれかで置けば良いようです。
Packaging libraries - Robot Framework User Guide

 
今回は、自作ライブラリをそのまま置くことにします。

 

ライブラリを使用するテストファイルを用意

自作したライブラリはSettingsで指定します。

なお、今回のテストファイルでは、ライブラリで定義したキーワードはどの程度まで認識されるかを調べるため、キーワードの区切り文字として

  • スペースなし
  • アンダースコア
  • 半角スペース
  • 全角スペース1つ
  • 全角スペース複数
  • タブ文字

をそれぞれ試してみます。

*** Settings ***
Library  MyLibrary


*** Test Cases ***
英語のキーワードをテストする
    # 見やすいように改行する
    Log To Console  ${EMPTY}

    # キーワード、引数の順(キーワードと引数の間は半角スペース2個空けている)
    hello_world  アンダースコア
    hello world  半角スペース
    helloworld  スペースなし


日本語のキーワードをテストする
    # 見やすいように改行する
    Log To Console  ${EMPTY}

    # キーワード、引数の順(キーワードと引数の間は半角スペース2個空けている)
    ハロー_ワールド  アンダースコア
    ハロー ワールド  半角スペース
    ハローワールド  スペースなし
    ハロー ワールド  全角スペース1個
    ハロー  ワールド  全角スペース2個
    ハロー   ワールド  タブ文字

 

実行

ファイルの配置

現在のファイルの配置は以下の通りです。

自作ライブラリとテストファイルを同じディレクトリに入れてあります。

$ pwd 
/path/to/create_library_sample

$ tree -L 1
.
├── MyLibrary.py
└── test_hello_using_my_library.robot

 

実行コマンド
robotコマンド

普通に robot コマンドで実行した場合、

$ robot test_hello_using_my_library.robot 
[ ERROR ] Error in file '/path/to/test_hello_using_my_library.robot': Importing test library 'MyLibrary' failed: ModuleNotFoundError: No module named 'MyLibrary'
Traceback (most recent call last):
  None
PYTHONPATH:
  /path/to/venv/bin
  /path/to/.pyenv/versions/3.6.3/lib/python36.zip
  /path/to/.pyenv/versions/3.6.3/lib/python3.6
  /path/to/.pyenv/versions/3.6.3/lib/python3.6/lib-dynload
  /path/to/venv/lib/python3.6/site-packages
==========================
Test Hello Using My Library
==========================
英語のキーワードをテストする
英語のキーワードをテストする    | FAIL |
No keyword with name 'hello_world' found.
------------------------------------
日本語のキーワードをテストする
日本語のキーワードをテストする    | FAIL |
No keyword with name 'ハロー_ワールド' found.
------------------------------------
Test Hello Using My Library    | FAIL |
2 critical tests, 0 passed, 2 failed
2 tests total, 0 passed, 2 failed
==========================

と、キーワードが見つからないというエラーになります。

PYTHONPATHなどで自作ライブラリが見つからないためです。

 

robotコマンド + --pythonpath (-P) オプション

Robot Frameworkの実行コマンド robot には、指定したパスをmodule search pathに追加してくれる --pythonpath (-P) オプションがあります。

試してみます。

$ robot -P . test_hello_using_my_library.robot 
===============================
Test Hello Using My Library
===============================
英語のキーワードをテストする
.アンダースコア de hello world
.半角スペース de hello world
.スペースなし de hello world
英語のキーワードをテストする    | PASS |
-------------------------------------------
日本語のキーワードをテストする
.アンダースコア を使ったハローワールド
.半角スペース を使ったハローワールド
.スペースなし を使ったハローワールド
.全角スペース1個 を使ったハローワールド
.全角スペース2個 を使ったハローワールド
日本語のキーワードをテストする    | FAIL |
No keyword with name 'ハロー' found.
-------------------------------------------
Test Hello Using My Library    | FAIL |
2 critical tests, 1 passed, 1 failed
2 tests total, 1 passed, 1 failed
===============================

自作ライブラリを認識してくれたようです。

ただ、キーワードが一つ失敗しています。タブ文字のキーワード指定っぽいです。そのため、

日本語のキーワードをテストする
#...
    #ハロー  ワールド  タブ文字

コメントアウトして再実行してみます。

$ robot -P . test_hello_using_my_library.robot 
===============================
Test Hello Using My Library
===============================
英語のキーワードをテストする
.アンダースコア de hello world
.半角スペース de hello world
.スペースなし de hello world
英語のキーワードをテストする    | PASS |
-------------------------------------------
日本語のキーワードをテストする
.アンダースコア を使ったハローワールド
.半角スペース を使ったハローワールド
.スペースなし を使ったハローワールド
.全角スペース1個 を使ったハローワールド
.全角スペース2個 を使ったハローワールド
日本語のキーワードをテストする    | PASS |
-------------------------------------------
Test Hello Using My Library    | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
===============================

テストをパスしました。

 

python -m robot で実行

Robot Frameworkは、Python-m コマンドラインオプションでも実行できます。

 
Python-m コマンドラインオプションは、

このオプションが指定された場合、 sys.argv の最初の要素はモジュールファイルのフルパスになります (モジュールファイルを検索している間、最初の要素は "-m" に設定されます)。 -c オプションと同様に、カレントディレクトリが sys.path の先頭に追加されます。

-m <module-name> - 1. コマンドラインと環境 — Python 3.6.3 ドキュメント

です。

そのため、テストファイルと自作ライブラリのあるディレクトリに移動してからテストを実行することで、自作ライブラリを認識します。

# ディレクトリへ移動
$ cd /path/to/create_library_sample

# ディレクトリの中身を確認
create_library_sample $ tree -L 1
.
├── MyLibrary.py
└── test_hello_using_my_library.robot

# -m オプションを付けて実行
$ python -m robot test_hello_using_my_library.robot 
=========================
Test Hello Using My Library
=========================
英語のキーワードをテストする
.アンダースコア de hello world
.半角スペース de hello world
.スペースなし de hello world
英語のキーワードをテストする    | PASS |
-----------------------------------
日本語のキーワードをテストする
.アンダースコア を使ったハローワールド
.半角スペース を使ったハローワールド
.スペースなし を使ったハローワールド
.全角スペース1個 を使ったハローワールド
.全角スペース2個 を使ったハローワールド
日本語のキーワードをテストする    | PASS |
-----------------------------------
Test Hello Using My Library    | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
=========================

 

ソースコード

GitHubに上げました。create_library_sample ディレクトリが今回のものです。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples