GitHubActionsでクリップボードに対するテストを書く
こんにちは。こんばんは。おはようございます。LAPASでエンジニアだったりDBのお守りだったりをしている denzow です。この記事はLAPRAS Advent Calendar 2021 5日目の記事です。
最近はredasql という redash に対してCLI経由でSQLを実行するツールを作っていました。その際のunittestをGitHub Actionsで実行しようとしたときに嵌った点についてあまり情報がなかったのでまとめておきます。
サンプルの説明
今回は、クリップボード周りの確認だけしたいので最小構成のアプリを用意しました。
import argparse
from pyperclip import copy
def init():
parser = argparse.ArgumentParser()
parser.add_argument(
'-t',
'--text',
help='text for clipboard',
required=True,
)
args = parser.parse_args()
return args.text
def main(text_for_clipboard: str):
copy(text_for_clipboard)
print(f'copied: {text_for_clipboard}')
if __name__ == '__main__':
text_for_clipboard = init()
main(text_for_clipboard)
こんな感じで-t コピーしたい文字列
として実行するとクリップボードに文字列が入ります。
(sample39) denzow@denpad$ poetry run python command.py -t hello
copied: hello
クリップボード周りはpyperclip を使っています。
そしてこちらに対する雑なテストコードです。
from unittest import TestCase
from pyperclip import paste
import command
class MainTest(TestCase):
def test_main(self):
"""
クリップボードに正常にコピーされるかのテスト
:return:
"""
expected_text = 'copy_target'
command.main(expected_text)
self.assertEqual(expected_text, paste())
ローカルでは正常にテストも実行できています。
(sample39) denzow@denpad$ poetry run python -m unittest -vvv
test_main (tests.test_command.MainTest)
クリップボードに正常にコピーされるかのテスト ... copied: copy_target
ok
----------------------------------------------------------------------
Ran 1 test in 0.006s
OK
これを題材に見ていきます。
GitHubActionsでのunittest実行
とりあえず何も考えずに適当にpoetryのcacheとか含めながらunittestを登録するとこんな感じになると思います。
name: unittest
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Python 3
uses: actions/setup-python@v1
with:
python-version: 3.9
- name: Install poetry
uses: snok/install-poetry@v1.0.0
with:
virtualenvs-create: true
virtualenvs-in-project: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v2
with:
path: .venv
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: poetry install
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
- name: unit test
run: |
poetry run python -m unittest discover -s tests/
さくっと実行するとさくっとに死にます。
======================================================================
ERROR: test_main (test_command.MainTest)
クリップボードに正常にコピーされるかのテスト
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/runner/work/github-actions-clipboard-sample/github-actions-clipboard-sample/tests/test_command.py", line 15, in test_main
command.main(expected_text)
File "/home/runner/work/github-actions-clipboard-sample/github-actions-clipboard-sample/command.py", line 18, in main
copy(text_for_clipboard)
File "/home/runner/work/github-actions-clipboard-sample/github-actions-clipboard-sample/.venv/lib/python3.9/site-packages/pyperclip/__init__.py", line 659, in lazy_load_stub_copy
return copy(text)
File "/home/runner/work/github-actions-clipboard-sample/github-actions-clipboard-sample/.venv/lib/python3.9/site-packages/pyperclip/__init__.py", line 336, in __call__
raise PyperclipException(EXCEPT_MSG)
pyperclip.PyperclipException:
Pyperclip could not find a copy/paste mechanism for your system.
For more information, please visit https://pyperclip.readthedocs.io/en/latest/index.html#not-implemented-error
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
Pyperclipが期待しているOS側のパッケージが足りてないとのことです。提示されているリンクを見ればわかりますがxsel
やxclip
が必要のようです。ソースではこの辺りです。
https://github.com/asweigart/pyperclip/blob/781603ea491eefce3b58f4f203bf748dbf9ff003/src/pyperclip/init.py#L576-L581
クリップボードに触れるコマンドラインツールを順に試して有るやつを使うって感じの実装ですね。とりあえず最初に試しているxsel
を先にインストールするようにします。
--- a/.github/workflows/unittest.yml
+++ b/.github/workflows/unittest.yml
@@ -17,6 +17,9 @@ jobs:
uses: actions/setup-python@v1
with:
python-version: 3.9
+ - name: Install os packages
+ run: |
+ sudo apt-get install -y xsel
- name: Install poetry
uses: snok/install-poetry@v1.0.0
with:
しかしこれでは結局同じエラーで失敗します。xsel
やxclip
を使うためにはDISPLAY
が必要ですがGitHubActionsで実行される環境には出力される画面が存在しないため結局利用できていません。
そこで仮想ディスプレイを作ってくれるxvfb
を利用します。xvfb
自体は仮想のXWindowを構築したりスクリーンショットをとったりと色々なことができますが、今回必要な部分の話だけであればxvfb-run
経由で仮想ディスプレイを利用させたいコマンドを実行させるだけです。
--- a/.github/workflows/unittest.yml
+++ b/.github/workflows/unittest.yml
@@ -19,7 +19,7 @@ jobs:
python-version: 3.9
- name: Install os packages
run: |
- sudo apt-get install -y xsel
+ sudo apt-get install -y xsel xvfb
- name: Install poetry
uses: snok/install-poetry@v1.0.0
with:
@@ -36,4 +36,4 @@ jobs:
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
- name: unit test
run: |
- poetry run python -m unittest discover -s tests/
+ xvfb-run poetry run python -m unittest discover -s tests/
これで無事にGitHubActions経由でもクリップボードのテストが出来るようになりました。
まとめ
長々と書きましたが、xsel
とxvfb
使えってだけの話でした。ただ、踏んだときにいい感じに情報を見つけられなかったので誰かの一助になれば幸いです。
合わせて今回のサンプルリポジトリを以下に公開しておきます。
Discussion