ã¯ããã«
ã¢ããªã±ã¼ã·ã§ã³ã»ãã¹ãã£ã³ã°ï¼ nikkieã§ãã
Command Line Interface Creation Kitãé æåãåã£ã¦Clickã
ã³ãã³ãã©ã¤ã³ãã¼ã«ãå®è£
ã§ããã©ã¤ãã©ãªã®1ã¤ã§ãã
Clickã®ããã¥ã¡ã³ãã®ä¸ã«ãã¹ãã®æ¸ãæ¹ãè¦ã¤ããæãåããã¦ã¿ã¾ããã
ç®æ¬¡
- ã¯ããã«
- ç®æ¬¡
- åä½ç°å¢
- ããã¥ã¡ã³ããTesting Click Applicationsã
- Basic Testingï¼ãã¹ãã®åºæ¬ï¼
- File System Isolationï¼ãã¡ã¤ã«ã·ã¹ãã åé¢ï¼
- Input Streamsï¼å ¥åã¹ããªã¼ã ï¼
- çµããã«
åä½ç°å¢
- Python 3.11.4
- Click 8.1.3
- ããã¥ã¡ã³ãã8.1.xãåç §ãã¾ãã https://click.palletsprojects.com/en/8.1.x/
- pytest 7.4.0
ããã¥ã¡ã³ããTesting Click Applicationsã
Clickã§ä½ãCLIã®ãã¹ãã®åºæ¬ã¨ãã¦ãclick.testingã¢ã¸ã¥ã¼ã«1ã«ã¤ãã¦è§£èª¬ããããã¥ã¡ã³ãã§ãã
æ±ã£ã¦ããã®ã¯æ¬¡ã®3ã¤ã
- Basic Testing
- File System Isolation
- Input Streams
é çªã«è¦ã¦ããã¾ãããã
Basic Testingï¼ãã¹ãã®åºæ¬ï¼
https://click.palletsprojects.com/en/8.1.x/testing/#basic-testing
click.testing.CliRunnerã使ãã¨ããåºæ¬ãç´¹ä»ããã¾ã
- CliRunnerã®
invokeã¡ã½ããã§Commandãå®è¡ã§ãã invokeã¡ã½ããã®è¿ãå¤ã¯click.testing.Resultåã§ããã¹ãã®ã¢ãµã¼ã·ã§ã³ã«å©ç¨ã§ãã
ãã¹ã対象ï¼helloã³ãã³ã
import click @click.command @click.argument("name") def hello(name): click.echo(f"Hello {name}!") if __name__ == "__main__": hello()
% python hello.py Peter Hello Peter!
helloã³ãã³ãã®ãã¹ã
CliRunnerã«invokeããã¾ãããã
from click.testing import CliRunner from hello import hello def test_hello_world(): runner = CliRunner() result = runner.invoke(hello, ["Peter"]) assert result.exit_code == 0 assert result.output == "Hello Peter!\n"
% pytest test_hello.py ============================= test session starts ============================== platform darwin -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0 rootdir: /Users/.../testing collected 1 item test_hello.py . [100%] ============================== 1 passed in 0.01s ===============================
ãµãã³ãã³ãã®ãã¹ã
å¥ã®ä¾ã¨ãã¦ãµãã³ãã³ãã«ãè¨åããã¾ãã
For subcommand testing, a subcommand name must be specified in the args parameter of CliRunner.invoke() method:
CliRunner.invokeã®ç¬¬1弿°ã«ã¯Groupã®cliãæ¸¡ãã¾ãã
ããã¦ã第2弿°ï¼æååã®ãªã¹ãï¼ã«ãµãã³ãã³ãåï¼syncï¼ãå«ãããªã¹ããæ¸¡ãã¾ãï¼
result = runner.invoke(cli, ['--debug', 'sync'])
File System Isolationï¼ãã¡ã¤ã«ã·ã¹ãã åé¢ï¼
https://click.palletsprojects.com/en/8.1.x/testing/#file-system-isolation
CliRunnerã®isolated_filesystemã¡ã½ããã使ããã¨ã§ãç¾å¨ã®ã¯ã¼ãã³ã°ãã£ã¬ã¯ããªãæ°ãã空ã®ãã£ã¬ã¯ããªã¨ãã¦æ±ãããã¨ããã¾ã2ã
ãã¹ã対象ï¼catã³ãã³ã
import click @click.command() @click.argument("f", type=click.File()) def cat(f): click.echo(f.read()) if __name__ == "__main__": cat()
% python cat.py input.txt
kokoro
aki
fuka
catã³ãã³ãã®ãã¹ã
isolated_filesystemã¡ã½ããã使ããã³ã³ããã¹ãããã¼ã¸ã£ã®ã¹ã³ã¼ãã«ã¦ãcatã³ãã³ãã«å
¥åãããã¡ã¤ã«ãæ¸ãè¾¼ãä¾ãç´¹ä»ããã¦ãã¾ãã
from click.testing import CliRunner from cat import cat def test_cat(): runner = CliRunner() with runner.isolated_filesystem(): with open("hello.txt", "w") as f: f.write("Hello World!") result = runner.invoke(cat, ["hello.txt"]) assert result.exit_code == 0 assert result.output == "Hello World!\n"
isolated_filesystemã¡ã½ããã«ããããã¹ãå®è¡æã®ã¯ã¼ãã³ã°ãã£ã¬ã¯ããªã空ã®ãã£ã¬ã¯ããªã®ããã«æ±ããã®ã§ãããã«hello.txtãä½ããç¸å¯¾ãã¹ã§catã³ãã³ãã«å
¥åããããã§ããã
% pytest test_cat.py ============================= test session starts ============================== platform darwin -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0 rootdir: /Users/.../testing collected 1 item test_cat.py . [100%] ============================== 1 passed in 0.01s ===============================
ç§ã¯ãã¹ãã«ä½¿ããã¼ã¿ï¼ãã£ã¯ã¹ãã£ï¼ã¨ãã¦ç¨æããã
好ã¿ã ã¨æãã¾ãããç§ã¯ãã¹ãå®è¡ä¸ã«æ¸ãè¾¼ãããã¯ãããããããã¹ãã«ä½¿ããã¼ã¿ã¨ãã¦ç¨æãã¦ããããã§ãã
from pathlib import Path from click.testing import CliRunner from cat import cat def test_cat(): test_root_dir = Path(__file__).resolve().parent input_path = test_root_dir / "fixtures" / "cat_input.txt" runner = CliRunner() result = runner.invoke(cat, [str(input_path)]) assert result.exit_code == 0 assert result.output == "Hello World!\n"
ã©ã¡ãã®æ¹æ³ã«ãã¡ãªããã»ãã¡ãªããã¯ããã¨æãã¾ãã
ãã¡ãªãã < ã¡ãªããã¨ãªãæ¹æ³ã使ãã®ãããã¨æãã¾ãï¼ç§ã®æèã§ã¯ãã¹ãã«ä½¿ããã¼ã¿ã¨ãã¦ç¨æããã»ãããã¡ãªãããå°ãããï¼
- ãã¹ãã«ä½¿ããã¼ã¿ï¼ãã£ã¯ã¹ãã£ï¼ã¨ãã¦ç¨æãã
- ã¡ãªããï¼ãã¹ãã«ä½¿ããã¼ã¿ãå¢ãã¦ãããã¹ãã¡ã½ããã¯ç°¡æ½ãªã¾ã¾ã§æ¸ã
- ãã¡ãªããï¼ãã£ã¯ã¹ãã£ã®ãã¹ã®åå¾ãï¼ç§ã«ã¨ã£ã¦ã¯ãã¤ã©ã¼ãã¬ã¼ãã§ã¯ãããï¼easyã§ã¯ãªã
isolated_filesystemã¡ã½ããã使ã- ã¡ãªããï¼ã«ã¬ã³ããã£ã¬ã¯ããªã空ã®ãã£ã¬ã¯ããªã¨ãã¦ä½¿ããã®ã§ãã³ãã³ãã«å ¥åãããã¡ã¤ã«ãã¹ã®æå®ãeasy
- ãã¡ãªããï¼ã³ãã³ãã®å ¥åã«ä½¿ããã¡ã¤ã«ã«5è¡ã»10è¡ã¨ãã¹ãã³ã¼ãã®ä¸ã§æ¸ãè¾¼ãã¨ããã¹ãã¡ã½ããï¼ã®arrangeé¨åï¼ãé·ããªããã¹ãèªä½ãèªã¿ã¥ãããªããã¡ã§ã¯ãªãã
ã¾ããã³ãã³ããåºåãããã¡ã¤ã«ã«ã¤ãã¦ã¯ãisolated_filesystemããããpytestã®ãã£ã¯ã¹ãã£ï¼tmp_path3ï¼ã使ãã°ããããã¨æãã¾ããã
Input Streamsï¼å ¥åã¹ããªã¼ã ï¼
https://click.palletsprojects.com/en/8.1.x/testing/#input-streams
å
¥åã«ã¯invokeã¡ã½ããã®input弿°ã使ãã¾ãã
ãã¹ã対象ï¼promptã³ãã³ã
import click @click.command() @click.option("--foo", prompt=True) def prompt(foo): click.echo(f"foo={foo}") if __name__ == "__main__": prompt()
optionã®prompt=Trueã«ã¤ãã¦ã¯ä»¥ä¸ãã©ããï¼
https://click.palletsprojects.com/en/8.1.x/options/#prompting
% python prompt.py Foo: wau wau foo=wau wau
ãFoo: ãã¨ä¿ãããã®ã§ãwau wauãã¨å ¥åãã¾ããã
promptã³ãã³ãã®ãã¹ã
from click.testing import CliRunner from prompt import prompt def test_prompt(): runner = CliRunner() result = runner.invoke(prompt, input="wau wau\n") assert result.exit_code == 0 assert result.output == "Foo: wau wau\nfoo=wau wau\n"
% pytest test_prompt.py ============================= test session starts ============================== platform darwin -- Python 3.11.4, pytest-7.4.0, pluggy-1.2.0 rootdir: /Users/.../testing collected 1 item test_prompt.py . [100%] ============================== 1 passed in 0.01s ===============================
çµããã«
Clickã®ããã¥ã¡ã³ããTesting Click Applicationsãã®3ã¤ã®ä¾ãè¦ã¦ãã¾ããã
click.testing.CliRunnerã®invokeã¡ã½ããã«ã³ãã³ããã°ã«ã¼ãã¨ã弿°ï¼æååï¼ã®ãªã¹ããæ¸¡ã- ãµãã³ãã³ãã®ãã¹ãã¯ã弿°ã®ãªã¹ãã§ãµãã³ãã³ãåãæå®
isolated_filesystemã§ãç¾å¨ã®ã¯ã¼ãã³ã°ãã£ã¬ã¯ããªã空ã®ãã£ã¬ã¯ããªã®ããã«æ±ãã- IMOï¼ç§ã«ã¯ããã¾ã使ãã©ãããªããã
- å ¥åãããã¡ã¤ã«ã¯ãï¼ãã¹ãã³ã¼ãã§æ¸ãè¾¼ãã®ã§ãªãï¼ãã¡ã¤ã«ã¨ãã¦ç¨æããã
- åºåãããã¡ã¤ã«ã¯ãpytestã®ä¸æãã£ã¬ã¯ããªãä½ããã£ã¯ã¹ãã£ã使ã£ã¦æå®ããã
- IMOï¼ç§ã«ã¯ããã¾ã使ãã©ãããªããã
invokeã¡ã½ããã¯input弿°ã§ãã³ãã³ãå®è¡ä¸ã«ããã³ããã§æ¸¡ãå¤ãæå®ã§ãã
Clickã§å®è£ ããã¢ããªã±ã¼ã·ã§ã³ã®ãã¹ãã¯ã©ãã¨æ¥ãï¼ã¨ããæ°æã¡ã§ãã
- APIãªãã¡ã¬ã³ã¹ãã½ã¼ã¹ã³ã¼ã↩
- ãthe CliRunner.isolated_filesystem() method is useful for setting the current working directory to a new, empty folder.ã↩
- https://docs.pytest.org/en/7.3.x/how-to/tmp_path.html↩