ð ãã®èšäºã¯ãããªæ¹ã«ãããã
- Seleniumã§Webãã©ãŒã ã®ãã¹ããèªååãããæ¹
- ãã°ã€ã³ãã©ãŒã ã®ããªããŒã·ã§ã³ãã¹ããç¶²çŸ çã«ãã§ãã¯ãããæ¹
- å¢çå€ã»ç¹æ®æåã»SQLã€ã³ãžã§ã¯ã·ã§ã³ãªã©ã®ãã¹ãã±ãŒã¹èšèšãåŠã³ããæ¹
- Pythonã§E2Eãã¹ããå®è£ ããŠã¿ããQAãšã³ãžãã¢ã®æ¹
â ãã®èšäºãèªããšãããããš
- Seleniumã§å š35ä»¶ã®ãã©ãŒã ããªããŒã·ã§ã³ãã¹ããèªåå®è¡ããæ¹æ³
- å šè§ã»åè§ã»å¢çå€ã»ç¹æ®æåã®ãã¹ãã±ãŒã¹èšèšãã¿ãŒã³
- FAILã±ãŒã¹ã®ã¹ã¯ãªãŒã³ã·ã§ããèªåä¿åã®å®è£ æ¹æ³
- ãã¹ãçµæãCSVã»JSONã¬ããŒããžåºåããæ¹æ³
ðšâð» çè ã«ã€ããŠ
QAãšã³ãžãã¢ãšããŠå®åã§SeleniumãæŽ»çšããèªåãã¹ããæ åœãæ¬èšäºã§ç޹ä»ããã¹ã¯ãªããã¯GitHubã§å ¬éããŠãããå®éã«åäœç¢ºèªæžã¿ã®ã³ãŒãããã®ãŸãŸè§£èª¬ããŠããŸãã
ãã°ã€ã³ãã©ãŒã ã¯å€ãã®Webã¢ããªã±ãŒã·ã§ã³ã§æãéèŠãªæ©èœã®äžã€ã§ããããªããŒã·ã§ã³ãæ£ããåäœããªãå ŽåããŠãŒã¶ãŒããã°ã€ã³ã§ããªãã ãã§ãªããSQLã€ã³ãžã§ã¯ã·ã§ã³ãäžæ£ã¢ã¯ã»ã¹ãšãã£ãé倧ãªã»ãã¥ãªãã£åé¡ã«çºå±ãããªã¹ã¯ããããŸããã ããããããªãªãŒã¹åã«ç¶²çŸ çãªãã¹ããèªååããŠããããšãéåžžã«éèŠã§ãã
ãã°ã€ã³ãã©ãŒã ã®ãã¹ãã£ãŠãæåã§ãããšå°å³ã«å€§å€ãããªãã§ããïŒå šè§æåã»ç©ºæåã»SQLã€ã³ãžã§ã¯ã·ã§ã³ã»çµµæåãŸã§ããã§ãã¯ãã¹ãé ç®ã¯å±±ã»ã©ãããŸãã
ãã®èšäºã§ã¯ãSeleniumã䜿ã£ãŠãã°ã€ã³ãã©ãŒã ã®ããªããŒã·ã§ã³ãã¹ããå š35ä»¶èªåå®è¡ããPythonã¹ã¯ãªããã解説ããŸãããã¹ãçµæã¯CSVã»JSONã§åºåã§ããFAILã±ãŒã¹ã¯ã¹ã¯ãªãŒã³ã·ã§ãããèªåä¿åãããŸãã
- ãã¹ã芳ç¹
- ãã¹ãã±ãŒã¹äžèЧïŒå š35ä»¶ïŒ
- ã»ããã¢ããã»å®è¡æ¹æ³
- å®è¡çµæãµã³ãã«
- ã³ãŒã解説ïŒäž»èŠãã€ã³ã3éž
- ã¬ããŒããCSVã»JSONã§èªååºåãã
- ããã«çºå±ãããã¢ã€ãã¢
- ããããã€ã³ã
- â send_keys() ã§ç¹æ®æåãæ£ããå ¥åã§ããªã
- â¡ HTML5ã® required 屿§ã§ãã©ãŠã¶ã«ãããã¯ããã
- â¢ å šä»¶ã¹ã¯ãªãŒã³ã·ã§ããã§ææ°ãèšå€§ã«ãªã£ã
- ⣠æåŸ å€ã®èšå®ãã¹ã§FAILãåºç¶ãã
- †ãšã©ãŒã¡ãã»ãŒãžã®ååŸã¿ã€ãã³ã°ãæ©ããã
- ãŸãšã
ãã¹ã芳ç¹
ä»åã®ã¹ã¯ãªããã§ã¯ããã°ã€ã³ãã©ãŒã ã«å¯ŸããŠä»¥äžã®èгç¹ãç¶²çŸ çã«ãã¹ãããŠããŸããQAã®çŸå Žã§ããã䜿ãããå®çªã®èгç¹ã°ãããªã®ã§ããã®ãŸãŸèªç€Ÿãããã¯ãã®ãã¹ãèšèšã«ãå¿çšã§ããŸãã
- ç©ºå ¥åãã§ãã¯ïŒãŠãŒã¶ãŒåã»ãã¹ã¯ãŒããæªå ¥åã®å Žåã«ãšã©ãŒãè¿ãã
- æåçš®ãã§ãã¯ïŒå šè§ã»æ¥æ¬èªã»çµµæåãªã©ãæ³å®å€ã®æåçš®ã匟ããã
- ã¡ãŒã«åœ¢åŒã»ã¹ããŒã¹æ··å ¥ïŒãã©ãŒãããéãã®å ¥åãæ£ããæåŠã§ããã
- å¢çå€ãã§ãã¯ïŒ1æåã»255æåãªã©ãé·ãã®ç«¯ç¹ã§ç°åžžãèµ·ããªãã
- ã»ãã¥ãªãã£ãã§ãã¯ïŒSQLã€ã³ãžã§ã¯ã·ã§ã³ã»XSSãªã©ã®æ»ææååãç¡å®³åã§ããã
- æ£åžžãã°ã€ã³ïŒæ£ããèªèšŒæ å ±ã§ãã°ã€ã³ãæåãããªãã€ã¬ã¯ããããã
ãã¹ãã±ãŒã¹äžèЧïŒå š35ä»¶ïŒ
ãã¹ãã±ãŒã¹ã¯4ã€ã®ã«ããŽãªã§æ§æãããŠããŸããå®åã§ããèŠèœãšãããã¡ãªå šè§æåãç¹æ®æåç³»ãæåãã«ããŒããŠããŸãã
| ã«ããŽãª | ID | ä»¶æ° | äž»ãªå 容 |
|---|---|---|---|
| å šè§æå | Z-01ãZ-08 | 8ä»¶ | å šè§è±åã»æ¥æ¬èªã»ã²ãããªã»ã«ã¿ã«ãã»å šè§èšå·ã»å šè§ã¹ããŒã¹ |
| åè§æå | H-01ãH-07 | 7ä»¶ | 倧æåã»å°æåã»æ°å€ã®ã¿ã»ã¹ããŒã¹æ··å ¥ã»ã¡ãŒã«åœ¢åŒ |
| é·ã / å¢çå€ | L-01ãL-10 | 10ä»¶ | 空æåã»1æåã»2æåã»3æåã»50æåã»255æå |
| ç¹æ®æå | S-01ãS-10 | 10ä»¶ | SQLã€ã³ãžã§ã¯ã·ã§ã³ã»XSSã»çµµæåã»æ¹è¡ã»ã¿ãã»URLãšã³ã³ãŒã |
â ïž ä»æããã€ã³ãïŒH-01ïŒæ£åžžãã°ã€ã³ïŒã¯ expected_error: True ãšæå³çã«ééããæåŸ
å€ãèšå®ããŠããŸããããã¯ãã¢çšã« FAILãçºçãããŠã¹ã¯ãªãŒã³ã·ã§ããä¿åã®åäœãç¢ºèª ããããã®ä»æãã§ããå®éã«äœ¿ãé㯠False ã«ä¿®æ£ããŸãããã
ã»ããã¢ããã»å®è¡æ¹æ³
å¿ èŠãªã©ã€ãã©ãªã®ã€ã³ã¹ããŒã«
以äžã®2ã€ã®ã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããã ãã§åäœããŸããChromeDriverã®æå管çã¯äžèŠã§ãã
pip install selenium webdriver-manager
å®è¡ã³ãã³ã
# ãã©ãŠã¶ã衚瀺ããŠå®è¡ïŒããã©ã«ãïŒ
python form_validation_test.py
# ãããã¬ã¹ã¢ãŒãïŒãã©ãŠã¶é衚瀺ã»CIç°å¢åãïŒ
python form_validation_test.py --headless
ð¡ TipïŒååå®è¡æã¯ webdriver-manager ãChromeDriverãèªåããŠã³ããŒãããŸãã2åç®ä»¥éã¯ãã£ãã·ã¥ã䜿ãããã®ã§é«éã§ãã
å®è¡çµæãµã³ãã«
å®è¡ãããšã¿ãŒããã«ã«ãªã¢ã«ã¿ã€ã ã§çµæãæµããŸããFAILã±ãŒã¹ã¯åå ãšäžç·ã«ã¹ã¯ãªãŒã³ã·ã§ããã®ãã¹ã衚瀺ãããŸãã
==============================================================
Selenium Form Validation Test (headless)
Target : //the-internet.herokuapp.com/login
Cases : 35
Start : 2025-07-01 10:00:00
==============================================================
(01/35) Full-width alpha username ... â
PASS (1823ms)
(02/35) Full-width numeric password ... â
PASS (1654ms)
(03/35) Full-width space in username ... â
PASS (1701ms)
...
(09/35) Valid login (correct creds) ... â FAIL (2100ms)
â expected_error:True / actual_error:False
ðž screenshots/H-01_FAIL.png
...
(35/35) URL-encoded characters ... â
PASS (1590ms)
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Test Summary
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Total : 35
â
PASS : 34
â FAIL : 1
â ïž ERROR : 0
Rate : 97.1%
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ã³ãŒã解説ïŒäž»èŠãã€ã³ã3éž
â JavaScriptçµç±ã§ãã©ãŒã ã«å€ãå ¥åãã
Seleniumã® send_keys() ã§ã¯æ¹è¡ïŒ\nïŒãã¿ãïŒ\tïŒãªã©ã®ç¹æ®æåãæ£ç¢ºã«å
¥åã§ããªãã±ãŒã¹ããããŸããJavaScriptã§çŽæ¥å€ãã»ããããããšã§ãããããæåãå®å
šã«å
¥åã§ããŸãã
arguments[0] ã«ã¯WebElementãarguments[1] ã«ã¯å
¥åãããæååãæž¡ããŸããããã«ããããã©ãŠã¶ã®ããŒã€ãã³ãåŠçãã¹ãããããŠDOMèŠçŽ ã«çŽæ¥å€ãæžã蟌ããŸããçµµæåã»å
šè§æåã»å¶åŸ¡æåã®ãããªãsend_keysã§ã¯åããããæåãã§ã確å®ã«åäœãããããç¹æ®æåç³»ãã¹ãã±ãŒã¹ïŒS-05ã®æ¹è¡ã»S-06ã®ã¿ãïŒã¯ãã®æ¹åŒããã£ãŠåããŠæç«ããŠããŸãã
driver.execute_script("arguments[0].value = arguments[1];", u_elem, tc["username"])
driver.execute_script("arguments[0].value = arguments[1];", p_elem, tc["password"])
â¡ HTML5ããªããŒã·ã§ã³ããã€ãã¹ãã
空æåãã¹ãïŒL-01ãL-03ïŒã¯HTML5ã® required 屿§ã«ãã£ãŠãã©ãŠã¶åŽã§ãããã¯ãããŠããŸããŸããnovalidate ãä»äžããããšã§ãªã¯ãšã¹ãããµãŒããŒãŸã§å±ããããµãŒããŒåŽã®æåãŸã§ç¢ºèªã§ããŸãã
ããã©ãŠã¶ããšã©ãŒãåºããªãïŒãã¹ãééãã§ã¯ãªããããµãŒããŒãæ£ãããšã©ãŒãè¿ããããŸã§ç¢ºèªããã®ãããªããŒã·ã§ã³ãã¹ãã®æ¬æ¥ã®ç®çã§ããããã³ããšã³ãã®ããªããŒã·ã§ã³ãæå³çã«ç¡å¹åããããšã§ãããã¯ãšã³ãã®å ç¢æ§ãçŽæ¥ãã¹ãã§ããŸãã
driver.execute_script(
"document.querySelector('form').setAttribute('novalidate','true');"
)
⢠FAILã±ãŒã¹ã ãã¹ã¯ãªãŒã³ã·ã§ãããä¿åãã
å šä»¶ã¹ã¯ãªãŒã³ã·ã§ãããæ®ããšææ°ãèšå€§ã«ãªããŸããFAILã®ãšãã ãä¿åããèšèšã«ããããšã§ãåŸããåå 確èªããããããªããŸãã
ãã¡ã€ã«å㯠{ãã¹ãID}_FAIL.png ã®åœ¢åŒã«ããŠãããããã©ã®ãã¹ãã±ãŒã¹ã§å€±æãããããã¡ã€ã«åã ãã§äžç®ã§ããããŸããGitHub Actionsãªã©ã®CI/CDç°å¢ã§ãã¹ããå®è¡ããå Žåãããã®ã¹ã¯ãªãŒã³ã·ã§ãããã¢ãŒãã£ãã¡ã¯ããšããŠä¿åããŠãããšãããã°å¹çãæ Œæ®µã«äžãããŸãã
if r.status == "FAIL":
os.makedirs("screenshots", exist_ok=True)
ss = f"screenshots/{tc['id']}_FAIL.png"
driver.save_screenshot(ss)
r.screenshot = ss
ð¡ å®åTipïŒPage Object ModelïŒPOMïŒã«ã€ããŠ
å®åã§ã¯ Page Object ModelïŒPOMïŒ ã䜿ãããã¹ãã³ãŒããšããŒãžæäœãåé¢ããèšèšãæšå¥šãããŠããŸããä»åã®ã¹ã¯ãªããã¯ã·ã³ãã«ãªæ§æã§ããããã¹ãã±ãŒã¹ãå¢ããŠããã LoginPage ã¯ã©ã¹ã«ãã©ãŒã æäœããŸãšããããšã§ãã¡ã³ããã³ã¹æ§ãšåå©çšæ§ã倧å¹
ã«åäžããŸãã
ã¬ããŒããCSVã»JSONã§èªååºåãã
ãã¹ãå®äºåŸãã¿ã€ã ã¹ã¿ã³ãä»ãã®ã¬ããŒããã¡ã€ã«ãèªåçæãããŸãã
ð report_20250701_100000.csv
ð report_20250701_100000.json
| åºååœ¢åŒ | äž»ãªçšé |
|---|---|
| CSV | Excelã§éããŠã¬ãã¥ãŒã»ãã£ã«ã¿ãªã³ã°ã»ããŒã å ±æã»ãšããã³ã¹æåº |
| JSON | GitHub Actions飿ºã»Slackéç¥ã»Allureã¬ããŒããžã®ããŒã¿é£æº |
ããã«çºå±ãããã¢ã€ãã¢
⡠䞊åå®è¡ã§é«éå
concurrent.futures ã䜿ã£ãŠãã©ãŠã¶ãè€æ°èµ·åãããã¹ãã䞊åå®è¡ããã°å®è¡æéã倧å¹
ã«ççž®ã§ããŸãã
ð ãªãã©ã€åŠçã®è¿œå
ãããã¯ãŒã¯ãäžå®å®ãªç°å¢ã§ãå®å®ããŠåäœãããããERRORã±ãŒã¹ã«èªåãªãã©ã€ããžãã¯ã远å ã§ããŸãã
ð ã¬ããŒãã®å¯èŠå
JSONã¬ããŒããããšã«AllureãHTMLã¬ããŒããçæããã°ãããŒã ãžã®å ±æãããã«ã¹ã ãŒãºã«ãªããŸãã
ð€ CI/CD飿º
GitHub ActionsãJenkinsã«çµã¿èŸŒãã§ãPRããšã«èªåãã¹ããå®è¡ããä»çµã¿ã«ã§ããŸãã
ð¡ æŽ»çšã·ãŒã³ã»æ¡åŒµäŸ
- ãªã°ã¬ãã·ã§ã³ãã¹ãã®äžç°ãšããŠæ¯ãªãªãŒã¹åã«å®è¡
- æ°æ©èœãªãªãŒã¹åã®ãã°ã€ã³æ©èœã®å¥å šæ§ãã§ãã¯
- ã¹ããŒãžã³ã°ç°å¢ã§ã®ã»ãã¥ãªãã£ç¢ºèªïŒSQLiã»XSSïŒ
- äŒå¡ç»é²ã»ãã¹ã¯ãŒã倿Žãã©ãŒã ãžã®æšªå±é
- ãã¹ãã±ãŒã¹ãCSVããèªã¿èŸŒã圢åŒã«å€æŽããŠããŒã³ãŒã管ç
- pytest-htmlãAllureãšçµã¿åãããŠã¬ããŒããèŠããå
ããããã€ã³ã
å®è£ äžã«å®éã«ã€ãŸãããç®æããŸãšããŸãããåããšããã§ãããæ¹ã®åèã«ãªãã°å¬ããã§ãã
â send_keys() ã§ç¹æ®æåãæ£ããå ¥åã§ããªã
æ¹è¡ïŒ\nïŒã»ã¿ãïŒ\tïŒã»çµµæåãªã©ã®ç¹æ®æåã send_keys() ã§å
¥åãããšãæååããããæå³ããªãåäœãèµ·ããããšããããŸãããç¹æ®æåã¯JavaScriptçµç±ã§çŽæ¥DOMã«æžã蟌ãããšã§è§£æ±ºããŸããã
# â send_keys()ã§ã¯ç¹æ®æåãåããããšããã
username_field.send_keys("test\nuser")
# â
JSçµç±ã§çŽæ¥å€ãã»ãããã
driver.execute_script("arguments[0].value = arguments[1];", username_field, "test\nuser")
ð¡ ãã€ã³ãïŒ arguments[0] ã«WebElementãarguments[1] ã«å
¥åãããæååãæž¡ãããšã§ããã©ãŠã¶ã®ããŒã€ãã³ãåŠçãã¹ãããããŠç¢ºå®ã«å€ãæžã蟌ããŸãã
â¡ HTML5ã® required 屿§ã§ãã©ãŠã¶ã«ãããã¯ããã
空æåãã¹ããå®è¡ããããšãããšããããã©ãŒã ã® required 屿§ã«ãã£ãŠãã©ãŠã¶åŽã§ãªã¯ãšã¹ããæ¢ãŸã£ãŠããŸãããµãŒããŒåŽã®æåã確èªã§ããŸããã§ããã
# â requiredããããšãã©ãŠã¶ããããã¯ããŠãµãŒããŒã«å±ããªã
submit_button.click()
# â
novalidateãä»äžããŠHTML5ããªããŒã·ã§ã³ããã€ãã¹
driver.execute_script(
"document.querySelector('form').setAttribute('novalidate', 'true');"
)
submit_button.click()
ð¡ ãã€ã³ãïŒ ããã©ãŠã¶ããšã©ãŒãåºããªãïŒãã¹ãééãã§ã¯ãªããããµãŒããŒãæ£ãããšã©ãŒãè¿ããããŸã§ç¢ºèªããã®ãããªããŒã·ã§ã³ãã¹ãã®æ¬æ¥ã®ç®çã§ãã
â¢ å šä»¶ã¹ã¯ãªãŒã³ã·ã§ããã§ææ°ãèšå€§ã«ãªã£ã
æåã¯å š35ä»¶ã§æ¯åã¹ã¯ãªãŒã³ã·ã§ãããæ®ã£ãŠãããšããããã¡ã€ã«ã倧éã«çæãããŠã©ããéèŠãªã®ãããããªããªããŸãããFAILã±ãŒã¹ã ãä¿åããèšèšã«å€ããããšã§ç®¡çãããããªããŸããã
# â å
šä»¶ä¿åãããšææ°ãèšå€§ã«ãªã
driver.save_screenshot(f"screenshots/{tc['id']}.png")
# â
FAILã®ãšãã ãä¿åãã
if result.status == "FAIL":
os.makedirs("screenshots", exist_ok=True)
driver.save_screenshot(f"screenshots/{tc['id']}_FAIL.png")
ð¡ ãã€ã³ãïŒ ãã¡ã€ã«åã {ãã¹ãID}_FAIL.png ã®åœ¢åŒã«ããŠãããšãã©ã®ãã¹ãã±ãŒã¹ã§å€±æãããããã¡ã€ã«åã ãã§äžç®ã§ããããŸãã
⣠æåŸ å€ã®èšå®ãã¹ã§FAILãåºç¶ãã
æ£åžžãã°ã€ã³ã®ãã¹ãã±ãŒã¹ïŒH-01ïŒã« expected_error: True ãšééããŠèšå®ããŠãããããæ£ãããã°ã€ã³ã§ããŠããã®ã«ãã£ãšFAILãšå€å®ãããŠããŸãããæåŸ
å€ã®èšå®ãã¹ã¯ãããã°ã«æ°ã¥ãã«ããã®ã§èŠæ³šæã§ãã
# â æåŸ
å€ãééã£ãŠãããšæ£åžžåäœã§ãFAILã«ãªã
{"id": "H-01", "username": "tomsmith", "password": "SuperSecretPassword!", "expected_error": True}
# â
æ£åžžãã°ã€ã³ã¯expected_error: Falseãæ£ãã
{"id": "H-01", "username": "tomsmith", "password": "SuperSecretPassword!", "expected_error": False}
â ïž æ³šæïŒ ãã¹ããFAILãããšãããŸããã³ãŒãã®ãã°ãããæåŸ å€ã®èšå®ãã¹ãããåãåããããšã倧äºã§ãã
†ãšã©ãŒã¡ãã»ãŒãžã®ååŸã¿ã€ãã³ã°ãæ©ããã
ãã©ãŒã éä¿¡çŽåŸã«ãšã©ãŒã¡ãã»ãŒãžãååŸããããšãããšããããŸã ç»é¢ãæŽæ°ãããŠããªããããšã©ãŒã¡ãã»ãŒãžãèŠã€ãããªãããšããããŸãããéä¿¡åŸã®åŸ æ©åŠçãå¿ èŠã§ããã
# â éä¿¡çŽåŸã«ãšã©ãŒãååŸããããšãããšèŠã€ãããªãããšããã
submit_button.click()
error = driver.find_element(By.ID, "flash")
# â
WebDriverWaitã§èŠçŽ ã衚瀺ããããŸã§åŸ
æ©ãã
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
submit_button.click()
error = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "flash"))
)
ð¡ ãã€ã³ãïŒ time.sleep() ã§åºå®åŸ
æ©ãããã WebDriverWait ã§æ¡ä»¶åŸ
æ©ããæ¹ãé«éã§å®å®ããŸãã
ãŸãšã
ãã®èšäºã§ã¯ãSeleniumã§ãã°ã€ã³ãã©ãŒã ã®ããªããŒã·ã§ã³ãã¹ããPythonã§èªååããæ¹æ³ã玹ä»ããŸããã
| ãã€ã³ã | å 容 |
|---|---|
| ãã¹ãã±ãŒã¹ | å šè§ã»åè§ã»å¢çå€ã»ç¹æ®æåã®4ã«ããŽãª èš35ä»¶ |
| å ¥åæ¹æ³ | JSçµç±ã§ã©ããªç¹æ®æåã確å®ã»å®å šã«å ¥å |
| ã¹ã¯ãªãŒã³ã·ã§ãã | FAILã±ãŒã¹ã®ã¿èªåä¿åïŒææ°ãçµã£ãŠç®¡çããããïŒ |
| ã¬ããŒãåºå | CSVã»JSON圢åŒã§ã¿ã€ã ã¹ã¿ã³ãä»ãèªåçæ |
| å®è¡ã¢ãŒã | –headless ãªãã·ã§ã³ã§CI/CDç°å¢ã«ãå¯Ÿå¿ |
å®åã§ã¯ãããã®ãã¹ããCI/CDãã€ãã©ã€ã³ã«çµã¿èŸŒã¿ããã«ãªã¯ãšã¹ãã®ãã³ã«èªåå®è¡ããããšã§å質ãç¶ç¶çã«æ€èšŒããŸããSeleniumãã¹ãã¯ååž°ãã¹ãã«ãåºã掻çšãããŠããããããã€ã®ãã³ã«ãã°ã€ã³æ©èœãæ£åžžã«åäœããŠããããèªåã§ç¢ºèªã§ããŸãã
GitHubã«ãœãŒã¹ã³ãŒããå ¬éããŠããŸãã®ã§ããã²èªåã®ç°å¢ã§ã詊ããŠã¿ãŠãã ããïŒãã©ãŒã ã®çš®é¡ãå€ããã ãã§ããŸããŸãªWebã¢ããªã®ãã¹ãã«å¿çšã§ããŸããäœãã質åãããã°ã³ã¡ã³ãæ¬ããã©ããð

