ä»åã¯ãJavaScriptã®ãã¹ããã¬ã¼ã ã¯ã¼ã¯ã¨ã³ã³ãã¼ãã³ãããã¹ãããããã®Reactã¦ã¼ãã£ãªãã£ã®çµã¿åããã¨ãã¦äººæ°ã®é«ããJestã¨React Testing Libraryã使ã£ã¦Reactã¢ããªã±ã¼ã·ã§ã³ããã¹ãããæ¹æ³ã«ã¤ãã¦èª¬æãã¾ãã
ãã¹ãã¨ã¯
ãã¹ãã¯ãã¢ããªã±ã¼ã·ã§ã³ã¨ããåããã¦ããã®ãã¹ã¦ã®æ©è½ã¨æ©è½ãæå³ããã¨ããã«åä½ãããã¨ã確èªããè¡çºã¾ãã¯ããã»ã¹ã§ãã
ã¾ãä¸è¬ã«ããæåãã¹ããã¨ãèªåãã¹ããã®2種é¡ã®ãã¹ããããã¾ãã
ã» æåãã¹ã
ï¼ æåãã¹ãã¯ããã¹ãã人éãæåã§å®è¡ããææ³ã§ãã
æåãã¹ãã¯ãç¹ã«æ©è½ãã¹ããåãå ¥ããã¹ããªã©ã®å ´åã«å©ç¨ããã¾ãã
æåãã¹ãã¯ãèªååãããã¨ãå°é£ãªå ´åããããããå¿ è¦ã«å¿ãã¦å©ç¨ããã¾ãã
ã» èªåãã¹ã
ï¼èªåãã¹ãã¯ããã¹ããèªååãããã¨ã«ããããã¹ãã®å¹çæ§ãåç¾æ§ãæ£ç¢ºæ§ãåä¸ãããããã®ææ³ã§ãã
ããã¯ããã¹ãã¹ã¯ãªãããèªåçã«å®è¡ãã¦ãã¢ããªã±ã¼ã·ã§ã³ã®æ¯ãèãã確èªãããã¨ã§å®ç¾ããã¾ãã
èªåãã¹ãã¯ãã¦ããããã¹ããçµåãã¹ããæ©è½ãã¹ããããã©ã¼ãã³ã¹ãã¹ããªã©ãæ§ã ãªç¨®é¡ã®ãã¹ãã«å©ç¨ããã¾ãã
èªåãã¹ãã¨æåãã¹ãã®ã©ã¡ããåªãã¦ãããã¯ã使ç¨ãããã¹ãã®ç¨®é¡ãç®çã«ãã£ã¦ç°ãªãã¾ãã
èªåãã¹ãã¯ç¹°ãè¿ãå®è¡ãããã¨ãã§ããæåãã¹ããããéããå¹ççã«å®è¡ã§ãã¾ãã
ä¸æ¹ãæåãã¹ãã¯ã人éã®ç®ãç´æãå©ç¨ãã¦ãããåºãç¯å²ã®ãã¹ãã±ã¼ã¹ãã«ãã¼ãããã¨ãã§ãã¾ãã
ã©ã¡ããé©åã«å©ç¨ãããã¨ã§ãé«å質ãªã½ããã¦ã§ã¢ã®éçºãæ¯æ´ãããã¨ãã§ãã¾ãã
èªåãã¹ãããªã¹ãã¨ãã¦ã¾ã¨ããã¨ã以ä¸ã®ãããªç¨®é¡ãããã¾ãã
ã» ã¦ããããã¹ã
ï¼åã
ã®ã³ã³ãã¼ãã³ããåç¬ã§ãã¹ããããã¨ãç®çã¨ãããã¹ãã§ããé常ã¯éçºè
ã«ãã£ã¦æ¸ãããããã°ã©ã ã³ã¼ãã¨ã¨ãã«ãã¼ã¸ã§ã³ç®¡çã·ã¹ãã ã«æ ¼ç´ããã¾ãã
ã» çµåãã¹ã
ï¼è¤æ°ã®ã³ã³ãã¼ãã³ããé£æºããéã®æ¯ãèãããã¹ãããææ³ã§ããè¤æ°ã®ã¦ããããã¹ããçµã¿åããã¦å®æ½ããããã¨ãããã¾ãã
ã» æ©è½ãã¹ã
ï¼ã½ããã¦ã§ã¢ã®æ©è½ãä»æ§æ¸éãã«åä½ãããã確èªãããã¹ãã§ããã¦ã¼ã¶ããªãã£ãã¦ã¼ã¶ã¨ã¯ã¹ããªã¨ã³ã¹ãªã©ã®å´é¢ãå«ã¾ãã¾ãã
ã» ããã©ã¼ãã³ã¹ãã¹ã
ï¼ã½ããã¦ã§ã¢ã®ããã©ã¼ãã³ã¹ãè©ä¾¡ãããã¹ãã§ããè² è·ãã¹ããã¹ãã¬ã¹ãã¹ãã容éãã¹ããªã©ã®ç¨®é¡ãããã¾ãã
ã» ã»ãã¥ãªãã£ãã¹ã
ï¼ã½ããã¦ã§ã¢ã®èå¼±æ§ãæ¤åºãããã¹ãã§ããä¾µå
¥ãã¹ããã¹ãã£ã³ãã¹ããèå¼±æ§è©ä¾¡ãªã©ã®ç¨®é¡ãããã¾ãã
ã» å¯ç¨æ§ãã¹ã
ï¼ã½ããã¦ã§ã¢ãé©åã«å©ç¨ã§ãããã確èªãããã¹ãã§ããå©ä¾¿æ§ãã¢ã¯ã»ã·ããªãã£ã親åæ§ãªã©ã®å´é¢ãå«ã¾ãã¾ãã
ã¤ã³ãã°ã¬ã¼ã·ã§ã³ãã¹ãï¼ç°ãªãã·ã¹ãã ãã¢ã¸ã¥ã¼ã«ãã³ã³ãã¼ãã³ããçµ±åãããéã®åä½ã確èªãããã¹ãã§ãã
ãããã®ç¨®é¡ã®èªåãã¹ããçµã¿åããã¦ãéçºããã»ã¹å ¨ä½ã§ã½ããã¦ã§ã¢ã®å質ã確ä¿ãããã¨ãã§ãã¾ãã
ã¾ããæåãã¹ãã«ã¯ã以ä¸ã®ãããªç¨®é¡ãããã¾ãã
ã» æ©è½ãã¹ã
ï¼ã½ããã¦ã§ã¢ã®æ©è½ãä»æ§æ¸éãã«åä½ãããã確èªãããã¹ãã§ãã
æ©è½ãã¹ãã«ã¯ãæ£å¸¸ç³»ãã¹ããç°å¸¸ç³»ãã¹ããå«ã¾ãã¾ãã
ã» åãå
¥ããã¹ã
ï¼ã½ããã¦ã§ã¢ãã¦ã¼ã¶ã¼ã®è¦ä»¶ã«åè´ãããã確èªãããã¹ãã§ãã
ã¦ã¼ã¶ã¼ãæãæ©è½ãè¦ä»¶ãæºãããã¦ãããã確èªãã¾ãã
ã» æé ãã¹ã
ï¼ã½ããã¦ã§ã¢ã®æä½æé ãæ£ãããã確èªãããã¹ãã§ãã
ã¦ã¼ã¶ã¼ãã©ã®ããã«ã½ããã¦ã§ã¢ãæä½ãããã確èªãã¾ãã
ã» äºææ§ãã¹ã
ï¼ã½ããã¦ã§ã¢ãç°ãªãç°å¢ããã©ãããã©ã¼ã ã§æ£å¸¸ã«åä½ãããã確èªãããã¹ãã§ãã
ç°ãªãOSããã©ã¦ã¶ãããã¤ã¹ãªã©ã§ãã¹ããè¡ãã¾ãã
ã» ã¦ã¼ã¶ããªãã£ãã¹ã
ï¼ã½ããã¦ã§ã¢ãå©ç¨ãããããã確èªãããã¹ãã§ãã
ã¦ã¼ã¶ã¼ãæä½ãããããç´æçã§ãããã確èªãã¾ãã
ã» ãã©ãã¯ããã¯ã¹ãã¹ã
ï¼å
é¨æ§é ãç¥ããã«ãå¤é¨ã®å
¥åã¨åºåããã¹ãããææ³ã§ãã
ä¸è¬çã«æ©è½ãã¹ãã«ä½¿ç¨ããã¾ãã
ã» ã°ã¬ã¼ããã¯ã¹ãã¹ã
ï¼å
é¨æ§é ãããç¨åº¦ç¥ã£ã¦ããç¶æ
ã§ããã¹ããå®æ½ããææ³ã§ãã
çµåãã¹ããã¤ã³ãã°ã¬ã¼ã·ã§ã³ãã¹ããªã©ã«ä½¿ç¨ããã¾ãã
ãããã®æåãã¹ãã¯ãèªåãã¹ãã¨çµã¿åããã¦ãéçºããã»ã¹å ¨ä½ã§ã½ããã¦ã§ã¢ã®å質ã確ä¿ãããã¨ãã§ãã¾ãã
æ¬æ¥ã¯ãèªåãã¹ãã®ã¦ããããã¹ãããã³çµåãã¹ãã®è§£èª¬ã¨ãªãã¾ãã®ã§ããã¡ãéç¹çã«è©³ãã解説è´ãã¾ãã
ã¦ããããã¹ãã¨çµåãã¹ã
ã» ã¦ããããã¹ã
ã¦ããããã¹ã(å義èª:åä½ãã¹ã)ã§ã¯ãåã ã®ã½ããã¦ã§ã¢ã¦ãããã¾ãã¯ã³ã³ãã¼ãã³ããåé¢ãã¦ãã¹ããã¾ãã
ãã¹ãããæ©è½ãã¢ã¸ã¥ã¼ã«ã®å ¥åå¤ãä¸ãããã®çµæãæå¾ éãã®åºåã«ãªããã©ãããæ¤è¨¼ãã¾ãã
ãã¹ãã®å®è¡ã¯èªååãããé常ã¯ç¹å®ã®ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ã使ç¨ãã¦è¡ããã¾ãã
ã¦ããããã¹ãã¯ãããã°ã©ã ã®ãã°ãæ©æã«çºè¦ããä¿®æ£ãããã¨ãã§ãããããã½ããã¦ã§ã¢éçºã«ããã¦é常ã«éè¦ãªå½¹å²ãæããã¦ãã¾ãã
ã¾ããã¦ããããã¹ããååã«å®æ½ãããã¨ã§ãããã°ã©ã å ¨ä½ã®å質ãåä¸ããããã¨ãã§ãã¾ãã
ã» çµåãã¹ã
çµåãã¹ã(å義èª: çµ±åãã¹ã)ã¯ãè¤æ°ã®ã½ããã¦ã§ã¢ã³ã³ãã¼ãã³ããçµåãã¦ã·ã¹ãã å ¨ä½ã®åä½ãæ¤è¨¼ãããã¹ãã®ãã¨ã§ãã
ã¤ã¾ããçµåãã¹ãã¯ãã³ã³ãã¼ãã³ããçµåãã¦åä½ããã·ã¹ãã ãä½æãããã®ã·ã¹ãã ãæ£ããæ©è½ãããã©ããããã¹ãããããã»ã¹ã§ãã
çµåãã¹ãã¯ãåã³ã³ãã¼ãã³ããåå¥ã«ãã¹ãããã¦ããããã¹ãããã§ã«å®äºãã¦ãããã¨ãåæã§ãã
ãããã®ã³ã³ãã¼ãã³ããçµåãã¦ã·ã¹ãã ãæ§ç¯ããã·ã¹ãã å ¨ä½ã®åä½ãæ¤è¨¼ãããã¨ã«ãã£ã¦ãã³ã³ãã¼ãã³ãéã®ã¤ã³ã¿ãã§ã¼ã¹ãç¸äºä½ç¨ã«åé¡ããªããã©ããã確èªãã¾ãã
çµåãã¹ãã«ã¯ãæ©è½çãªãã¹ããæ§è½ãã¹ããä¿¡é ¼æ§ãã¹ããªã©ããã¾ãã¾ãªç¨®é¡ãããã¾ãã
ããã°ã©ãã³ã°ã«ããçµåãã¹ãã¯ãã½ããã¦ã§ã¢ã®å質ã確ä¿ããããã«é常ã«éè¦ã§ãã
ã·ã¹ãã å ¨ä½ã®åä½ãæ¤è¨¼ãããã¨ã§ãã³ã³ãã¼ãã³ãã®éã§çããåé¡ãæ©æã«çºè¦ããä¿®æ£ãããã¨ãã§ãã¾ãã
ããã«ãããã·ã¹ãã ã®å質ãåä¸ãããã¦ã¼ã¶ã¼ãæå¾ ããæ§è½ãä¿¡é ¼æ§ã確ä¿ãããã¨ãã§ãã¾ãã
ã¢ããªã±ã¼ã·ã§ã³ã®ã³ã³ãã¼ãã³ããã©ã®ããã«é£æºãããããã¹ãã§ãã¾ãã
ãã¹ãã«ãã¬ãã¸ã§çç¥ãã¹ããã®
ãã¹ãã«ãã¬ãã¸ã¯ãã½ããã¦ã§ã¢ã®ãã¹ããè¨ç»ãè¨è¨ãå®è¡ãå ±åããããã®éè¦ãªææ¨ã§ãããéçºè ãå質管çããã»ã¹ãæ¹åããããã®æ å ±ãæä¾ãã¾ãã
å ·ä½çã«ã¯ãã³ã¼ãã«ãã¬ãã¸ã¬ãã¼ããçæãããã¨ã«ãã£ã¦ããã¹ããããè¡ãåå²ãæ¡ä»¶ãªã©ã®ã¹ãã¼ãã¡ã³ãã®å²åã示ãã¾ãã
Reactã®ãã¹ãã«ãã¬ãã¸ãçç¥ãããã©ããã¯ãããã¸ã§ã¯ãã®è¦ä»¶ãç®æ¨ã«ãã£ã¦ç°ãªãã¾ãã
ãã ããä¸è¬çã«ã¯ä»¥ä¸ã®ãããªãã®ãçç¥ãããã¨ãããã¾ãã
1. ãã¹ãããªãã¦ãå®å®ãã¦åä½ããã³ã¼ã
ãã¹ãã«ãã¬ãã¸ãé«ãããã¨ã¯éè¦ã§ãããå®å®ãã¦åä½ããã¨ããã£ã¦ããã³ã¼ãã¯æéã®ç¡é§ã«ãªãå¯è½æ§ãããã¾ãã
ãã®ããããã¹ããçç¥ãã¦ãåé¡ããªãã¨æãããå ´åã¯ããã¹ããè¡ããªããã¨ãããã¾ãã
2. ã©ã¤ãã©ãªããã¬ã¼ã ã¯ã¼ã¯ã«ä¾åããã³ã¼ã
ã©ã¤ãã©ãªããã¬ã¼ã ã¯ã¼ã¯ã®ãã¹ãã¯ããããã®éçºè ã«ãã£ã¦æ¢ã«å®æ½ããã¦ããå¯è½æ§ãããã¾ãã
ãã®ãããä¾åããã³ã¼ãããã¹ããããã¨ã¯ãã¾ãæå³ããªãå ´åãããã¾ãã
3. ã¦ã¼ã¶ã¼ã¤ã³ã¿ã¼ãã§ã¼ã¹ã«é¢é£ããã³ã¼ã
ã¦ã¼ã¶ã¼ã¤ã³ã¿ã¼ãã§ã¼ã¹ã®å¤æ´ãé »ç¹ã«çºçããå ´åããã¹ããè¡ã£ã¦ãæå³ããªããªãå¯è½æ§ãããã¾ãã
ãã®ãããã¦ã¼ã¶ã¼ã¤ã³ã¿ã¼ãã§ã¼ã¹ã«é¢é£ããã³ã¼ãã®ãã¹ãã¯çç¥ããããã¨ãããã¾ãã
ãã ãããããã®å ´åã§ãããã¹ãã«ãã¬ãã¸ãçç¥ãããã¨ã常ã«è¯ãããã§ã¯ããã¾ããã
ããã¸ã§ã¯ãã®ç®æ¨ãè¦ä»¶ãã³ã¼ãã®å質ã«å¿ãã¦ãé©åãªãã¹ãã«ãã¬ãã¸ãè¨å®ããå¿ è¦ãããã¾ãã
Jestã¨ã¯ï¼
Jestã¯ãJavaScriptã®ä¸è¬çãªãã¹ããã¬ã¼ã ã¯ã¼ã¯ã§ãã
ã¦ããããã¹ãã§æãä¸è¬çã«ä½¿ç¨ããã¦ãããè¿å¹´ã¯ããã³ãã¨ã³ãã¨ããã¯ã¨ã³ãã®ä¸¡æ¹ã®ãã¹ãã§äººæ°ãé«ã¾ã£ã¦ãã¾ãã
å¤ãã®å¤§ä¼æ¥ãReactãã¹ãã«Jestãæ¡ç¨ãã¦ããããTwitterãããInstagramãããPinterestãããAirbnbããªã©ããã®ä¸ä¾ã§ãã
Jestã¯ãå®éã«ã¯ã©ã¤ãã©ãªã§ã¯ãªããã¬ã¼ã ã¯ã¼ã¯ã§ããããã¹ãã©ã³ãã¼ãã¢ãµã¼ã·ã§ã³ã©ã¤ãã©ãªãCLIãã¼ã«ãããã³æ§ã ãªã¢ãã¯ææ³ã®ãµãã¼ããæä¾ãã¾ãã
ããããã¹ã¦ããåãªãã©ã¤ãã©ãªã§ã¯ãªãããã¬ã¼ã ã¯ã¼ã¯ã«ãªãã¾ãã
Jestã®ä¸»ãªç¹å¾´ã¯ä»¥ä¸ã®éãã§ãã
ã» ã¼ãæ§æ
ï¼ Jestã¯ãJavaScriptã®ã»ã¨ãã©ã®ããã¸ã§ã¯ãã§æ§æãªãã§åä½ãããã¨ãç®æãã¦ãã¾ãã
ããã¸ã§ã¯ãã®ä¾åé¢ä¿ã«Jestãã¤ã³ã¹ãã¼ã«ããã ãã§ã調æ´ãªãã¾ãã¯æå°éã®èª¿æ´ã§æåã®ãã¹ããä½æã§ãã¾ãã
ã» åé¢ããããã¹ã
ï¼ãã¹ãã®åé¢ã¯é常ã«éè¦ãªããããã£ã§ãã
ç°ãªããã¹ããäºãã®çµæã«å½±é¿ãä¸ããªãããã«ãJestã¯ãã¹ãã並åå®è¡ãããã¨ãã§ãã¾ãã
ããã«ãããå®è¡æéãç縮ããéçºè ãããè¿ éã«ãã£ã¼ãããã¯ãå¾ããã¨ãã§ãã¾ãã
ã» ã¹ãããã·ã§ãã
ï¼ Jestã«ã¯ã¹ãããã·ã§ãããã¹ããããã¾ãã
ã¹ãããã·ã§ããã¯ä¿åãããã¹ãããã·ã§ããã¨ç §åãããä¸è´ããæ©è½ããã§ãã¯ããã¾ãã
ããã«ããããªãã¸ã§ã¯ãã®ããããã£ãåå¨ããæ£ããåãæã£ã¦ãããã©ããã確èªããããã«ãã¢ãµã¼ã·ã§ã³ã§å¤§éã®ãã¹ããä½æããå¿ è¦ããªããªãã¾ãã
ã¹ãããã·ã§ãããã¹ãã¯ãReactã³ã³ãã¼ãã³ãã®UIã®ã¹ãããã·ã§ããããã§ãã¯ããããã«ä½¿ç¨ãããã¨ãã§ãã¾ãã
Reactã³ã³ãã¼ãã³ãã®ã¹ãããã·ã§ãããæ®å½±ããã¨ãReactãDOMã«æç»ããåã«ãã³ã³ãã¼ãã³ãã®åºåãéçãªãã¼ã¯ã¢ããã«ã·ãªã¢ã«åãã¾ãã
ãã®å¾ãå¾ç¶ã®ãã¹ãã§ãæ°ããã¹ãããã·ã§ããã以åã®ãã®ã¨ä¸è´ãããã©ãããæ¤è¨¼ãããã¨ãã§ãã¾ãã
ããã«ãããUIã®å¤æ´ã«å¯¾ãã¦è¿ éãªãã¹ããå®è¡ã§ãã¾ãã
ã¹ãããã·ã§ãããã¹ãã¯é常ã«ä¾¿å©ã§ããã注æãå¿ è¦ã§ãã
ã¹ãããã·ã§ãããå¤æ´ãããå ´åããã¹ãã失æãããããéçºè ã¯ãã®å¤æ´ãåãå ¥ãããæå¦ãããã決å®ããå¿ è¦ãããã¾ãã
ãããã®ããã«ãã·ã³ãã«ãã念é ã«ç½®ãã¦è¨è¨ããããã¬ã¼ã ã¯ã¼ã¯ã§ãããåé¢ããããã¹ããã¹ãããã·ã§ããã®æ¯è¼ãã¢ãã¯ããã¹ãã«ãã¬ãã¸ãªã©ãæ§ç¯ããããã®å¼·åã§æ´ç·´ãããAPIãæä¾ãã¾ãã
React Testing Library(RTL)ã¨ã¯ï¼
React Testing Library(以ä¸RTL)ã¯ãReactã³ã³ãã¼ãã³ãããã¹ãããããã«ç¹å¥ã«æ§ç¯ããã JavaScriptãã¹ãã¦ã¼ãã£ãªãã£ã§ãã
åé¢ãããã³ã³ãã¼ãã³ãã§ã¦ã¼ã¶ã¼ã®æä½ãã·ãã¥ã¬ã¼ããããããã®åºåãã¢ãµã¼ããã¦ãUIãæ£ããåä½ãã¦ãããã¨ã確èªãã¾ãã
ã¾ããRTLã¯Jestã«ä»£ãããã®ã§ã¯ããã¾ããã
ãããããæ確ãªã¿ã¹ã¯ãå®è¡ããã³ã³ãã¼ãã³ãããã¹ãããã«ã¯ä¸¡æ¹ãå¿ è¦ã§ãã
Reactã®ãã¹ãã©ã¤ãã©ãªã¯å½ããã°ã®è¨äºã§è©³ãã解説ãã¦ãã¾ãã®ã§ä»¥ä¸ãå ã«åç §ãã ããã
RTLã«ã¤ãã¦è§¦ãã¦ããã¾ãã
ã¦ããããã¹ãã®æ§ç¯
ã¾ãã¯ãReactã¢ããªãJestã¨React Testing Libraryã使ç¨ãã¦ãReactã³ã³ãã¼ãã³ãã®ã¦ããããã¹ã(åä½ãã¹ã)ãè¡ãæ¹æ³ãå¦ç¿ãã¦ããã¾ãããã
Reactã³ã³ãã¼ãã³ãã®ã¦ããããã¹ãã¯ãåå¿è ãæ¢ã«ãã¹ãã«åãçµãã§ããçµé¨è±å¯ãªéçºè ã«ã¨ã£ã¦ã¯é£ããããããã¾ããã
ã©ã®Reactã³ã³ãã¼ãã³ãããã¹ãããå¿ è¦ãããããã³ã³ãã¼ãã³ãå ã®ã©ã®ã³ã¼ããæ£ç¢ºã«ã«ãã¼ããå¿ è¦ãããããç¥ãå¿ è¦ãããã¾ãã
ããã§ã¯ãReactã¢ããªãä½æãã¾ãã
Create-React-Appã使ç¨ãã¦ããreact-jest-tutorialãã¨ããååã®æ°ããããã¸ã§ã¯ããä½æãã¾ãã
ã¿ã¼ããã«ã§ä»¥ä¸ã®ã³ãã³ããå®è¡ãã¦ãã ããã
npx create-react-app react-jest-tutorial cd react-jest-tutorial
次ã«ããã¹ãã©ã³ãã¼ã¨ãã¦Jestãã¤ã³ã¹ãã¼ã«ãã¾ãã
Jestã¯ãJavaScriptã¢ããªã±ã¼ã·ã§ã³ã®èªåãã¹ããè¡ãããã®ãã¬ã¼ã ã¯ã¼ã¯ã§ãã
以ä¸ã®ã³ãã³ãã使ç¨ãã¦ãJestãã¤ã³ã¹ãã¼ã«ãã¦ãã ããã
npm install jest @testing-library/react --save-dev
ããã§ã¯ãJestã¨å ±ã«React Testing Libraryãã¤ã³ã¹ãã¼ã«ãã¾ãã
RTLã¯ãReactã³ã³ãã¼ãã³ãããã¹ãããããã®ã¦ã¼ãã£ãªãã£é¢æ°ãæä¾ãã¾ãã
Jestã®ã¹ãããã·ã§ãããã¹ãã«ã対å¿ãã¦ãã¾ãã
Jestã¨React Testing Libraryï¼RTLï¼ã使ç¨ãã¦ãReactã¢ããªã®ã¦ããããã¹ããè¡ãããã®åºæ¬çãªã³ã¼ãä¾ã示ãã¾ãã
ã¾ãã¯ãã«ã¦ã³ã¿ã¼ã³ã³ãã¼ãã³ããä½æãã¾ãã
import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); function incrementCount() { setCount(count + 1); } function decrementCount() { setCount(count - 1); } return ( <div> <h1>Counter</h1> <p>Count: {count}</p> <button onClick={incrementCount}>Increment</button> <button onClick={decrementCount}>Decrement</button> </div> ); }
ãã®ã³ã³ãã¼ãã³ãã§ã¯ãuseStateããã¯ã使ç¨ãã¦ãç¾å¨ã®ã«ã¦ã³ã¿ã¼ã®å¤ã管çãã¦ãã¾ãã
ãincrementCountãã¨ãdecrementCountãé¢æ°ã¯ãã«ã¦ã³ã¿ã¼ã®å¤ãå¢å ããã³æ¸å°ãããããã«ä½¿ç¨ããã¾ãã
æå¾ã«ãreturnæã§ãã«ã¦ã³ã¿ã¼ã®å¤ã表示ããpã¿ã°ã¨ãã¤ã³ã¯ãªã¡ã³ãããã³ãã¯ãªã¡ã³ãããããã®2ã¤ã®ãã¿ã³ãã¬ã³ããªã³ã°ãã¦ãã¾ãã
ããã§ã¯ããã®åç´ãªã«ã¦ã³ã¿ã¼ã³ã³ãã¼ãã³ããã¦ããããã¹ããã¾ãã
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import Counter from './Counter'; test('increments the counter', () => { // ãã¹ãç¨ã®å¤æ°ãä½æããã«ã¦ã³ã¿ã¼ã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ãã const { getByText } = render(<Counter />); // ã«ã¦ã³ã¿ã¼ãã¤ã³ã¯ãªã¡ã³ããããã¿ã³ãåå¾ããã¯ãªãã¯ã¤ãã³ããçºçããã const incrementButton = getByText('Increment'); fireEvent.click(incrementButton); // ã«ã¦ã³ã¿ã¼ã®å¤ã1ã§ãããã¨ã確èªãã const counterValue = getByText('Count: 1'); expect(counterValue).toBeInTheDocument(); }); test('decrements the counter', () => { // ãã¹ãç¨ã®å¤æ°ãä½æããã«ã¦ã³ã¿ã¼ã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ãã const { getByText } = render(<Counter />); // ã«ã¦ã³ã¿ã¼ããã¯ãªã¡ã³ããããã¿ã³ãåå¾ããã¯ãªãã¯ã¤ãã³ããçºçããã const decrementButton = getByText('Decrement'); fireEvent.click(decrementButton); // ã«ã¦ã³ã¿ã¼ã®å¤ã-1ã§ãããã¨ã確èªãã const counterValue = getByText('Count: -1'); expect(counterValue).toBeInTheDocument(); });
ä¸è¨ã®ä¾ã§ã¯ãã«ã¦ã³ã¿ã¼ã³ã³ãã¼ãã³ãã«å¯¾ãã¦2ã¤ã®ãã¹ããè¡ããã¾ãã
æåã®ãã¹ãã¯ãã«ã¦ã³ã¿ã¼ãã¤ã³ã¯ãªã¡ã³ããããã¿ã³ãã¯ãªãã¯ããéã«ãã«ã¦ã³ã¿ã¼ã®å¤ãæ£ããå¢å ãããã©ããã確èªãã¾ãã
2çªç®ã®ãã¹ãã¯ãã«ã¦ã³ã¿ã¼ããã¯ãªã¡ã³ããããã¿ã³ãã¯ãªãã¯ããéã«ãã«ã¦ã³ã¿ã¼ã®å¤ãæ£ããæ¸å°ãããã©ããã確èªãã¾ãã
ã©ã¡ãã®ãã¹ãããrenderã¡ã½ããã使ç¨ãã¦ãã«ã¦ã³ã¿ã¼ã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ãããgetByTextãã¡ã½ããã使ç¨ãã¦ããã¿ã³ãåå¾ãã¾ãã
ãã®å¾ããfireEventãã¡ã½ããã使ç¨ãã¦ããã¿ã³ãã¯ãªãã¯ãããexpectãã¡ã½ããã使ç¨ãã¦ãã«ã¦ã³ã¿ã¼ã®å¤ãæ£ãããã©ããã確èªãã¾ãã
æå¾ã«ããnpx jestãã³ãã³ãã使ç¨ãã¦ããã¹ããå®è¡ãã¾ãã
npx jest
Jestã¯ããã¹ããå®è¡ããã©ã®ãã¹ããåæ ¼ããã©ã®ãã¹ãã失æããããå ±åãã¾ãã
ã¾ãããnpm testãã³ãã³ãã§ãåé¡ããã¾ããã
npm test
ãnpm testãã³ãã³ãã¯ããpackage.json ããã¡ã¤ã«å ã®testã¹ã¯ãªãããå®è¡ããããã®ã·ã§ã¼ãã«ããã§ãã
é常ãtestã¹ã¯ãªããã¯ãnpx jestããå®è¡ããããã«è¨å®ããã¦ãã¾ãã
ãããã£ã¦ããnpx jestãã³ãã³ããå®è¡ãã¦ãåãçµæãå¾ããã¾ãã
ã¾ããJestã®æ§æãã¡ã¤ã«ã«æåã§ãã¹ããã¡ã¤ã«ã®ãã¹ã追å ããå¿ è¦ã¯ããã¾ããã
Jestã¯ãããã©ã«ã㧠__tests__
ãã£ã¬ã¯ããªã¾ã㯠*.test.js / *.spec.js
ã®ãã¡ã¤ã«ãèªåçã«æ¤åºãããã¹ããã¡ã¤ã«ã¨ãã¦å®è¡ãã¾ãã
ããã¦ãJestã¯ã *.test.js / *.spec.js
ãã¡ã¤ã«ã®å ´æã«ããã½ã¼ã¹ãã¡ã¤ã«ã¨åãå ´æã«ããã¢ã¸ã¥ã¼ã«ãèªåçã«ã¤ã³ãã¼ããã¾ãã
ãããã£ã¦ããã¹ããã¡ã¤ã«ãæ£ãããã£ã¬ã¯ããªã«ç½®ãããæ£ãããã¡ã¤ã«åãä»ãããã¦ããéããæåã§ãã¹ããã¡ã¤ã«ã®ãã¹ãæ§æãã¡ã¤ã«ã«è¿½å ããå¿ è¦ã¯ããã¾ããã
æåã§ãã¹ããã¡ã¤ã«ã®ãã¹ãJestã®æ§æãã¡ã¤ã«ã«è¿½å ããã«ã¯ãjest.config.js
ãã¡ã¤ã«ã«ã¯ã testMatch
ãªãã·ã§ã³ã使ç¨ãã¦ããã¹ããã¡ã¤ã«ã®æ£è¦è¡¨ç¾ãã¿ã¼ã³ãæå®ãããã¨ãã§ãã¾ãã
ãã¨ãã°ã testMatch
ã以ä¸ã®ããã«è¨å®ãããã¨ã§ããtests ããã£ã¬ã¯ããªã«ãã.spec.js
æ¡å¼µåã®ãã¡ã¤ã«ããã¹ããã¡ã¤ã«ã¨ãã¦å®è¡ã§ãã¾ãã
// jest.config.js module.exports = { testMatch: [ "**/tests/**/*.spec.js" ] };
ãã®è¨å®ã¯ã testsãã£ã¬ã¯ããªã¨ãã®ãµããã£ã¬ã¯ããªã«ãããã¹ã¦ã® .spec.js
æ¡å¼µåã®ãã¡ã¤ã«ããã¹ããã¡ã¤ã«ã¨ãã¦å®è¡ãã¾ãã
次ã¯ãã¦ããããã¹ãã®å¿ç¨ç·¨ã§ããButtonã³ã³ãã¼ãã³ãã®ãã¹ããã¡ã¤ã«ã§ãããButton.jsããä½æãã¾ãã
// Button.js // Buttonã³ã³ãã¼ãã³ããã¤ã³ãã¼ã import Button from './Button'; // React Testing Libraryããrenderã¨fireEventãã¤ã³ãã¼ã import { render, fireEvent } from '@testing-library/react'; // æåã®ãã¹ãã±ã¼ã¹: ãã¿ã³ãæ£ããã©ãã«ãæã¤ãã©ããããã¹ããã test('renders a button with the correct label', () => { // Buttonã³ã³ãã¼ãã³ãã"Click me"ã¨ããã©ãã«ã§ã¬ã³ããªã³ã°ãã const { getByText } = render(<Button label="Click me" />); // ãã¿ã³ã®ã©ãã«ã"Click me"ã§ãããã¨ã確èªãã const button = getByText('Click me'); expect(button).toBeInTheDocument(); }); // 次ã®ãã¹ãã±ã¼ã¹: ãã¿ã³ãã¯ãªãã¯ãããæã«onClickãã³ãã©ãå¼ã³åºããããã©ããããã¹ããã test('calls the onClick handler when clicked', () => { // onClickãã³ãã©ãã¢ãã¯ãã const onClick = jest.fn(); // Buttonã³ã³ãã¼ãã³ãã"Click me"ã¨ããã©ãã«ã§ã¬ã³ããªã³ã°ããonClickãã³ãã©ã渡ã const { getByText } = render(<Button label="Click me" onClick={onClick} />); // ãã¿ã³ãã¯ãªãã¯ãã const button = getByText('Click me'); fireEvent.click(button); // onClickãã³ãã©ã1åã ãå¼ã³åºããããã¨ã確èªãã expect(onClick).toHaveBeenCalledTimes(1); });
ãã®ã³ãã³ããå®è¡ããã¨ãJestãèªåçã«ã./srcããã©ã«ãå ã®ãã¹ã¦ã®ãã¹ããã¡ã¤ã«ãæ¢ãã¦ããã¹ããå®è¡ãã¾ãã
ãã¹ããå®äºããã¨ãçµæãã³ãã³ãã©ã¤ã³ã«è¡¨ç¤ºããã¾ãã
ä¸è¨ã®ã³ã¼ãã§ã¯ãButtonã³ã³ãã¼ãã³ãããã¹ãããããã«ã2ã¤ã®ãã¹ããä½æãã¾ããã
æåã®ãã¹ãã¯ãButtonã³ã³ãã¼ãã³ããæ£ããã©ãã«ãæã¤ãã¿ã³ãã¬ã³ããªã³ã°ãããã©ããã確èªãã¦ãã¾ãã
2çªç®ã®ãã¹ãã¯ããã¿ã³ãã¯ãªãã¯ãããã¨ãã«onClickãã³ãã©ãå¼ã³åºããããã©ããã確èªãã¦ãã¾ãã
ãã¹ãã«ã¯ãã@testing-library/reactãã©ã¤ãã©ãªã®ãrenderãé¢æ°ã¨ãfireEventãé¢æ°ã使ç¨ããã¦ãã¾ãã
ãrenderãé¢æ°ã¯ãReactã³ã³ãã¼ãã³ããä»®æ³DOMã«ã¬ã³ããªã³ã°ããããã«ä½¿ç¨ããã¾ãã
ãfireEventãé¢æ°ã¯ãã¦ã¼ã¶ã¼ã®ã¢ã¯ã·ã§ã³ï¼ä¾ãã°ãã¯ãªãã¯ãªã©ï¼ãã·ãã¥ã¬ã¼ãããããã«ä½¿ç¨ããã¾ãã
æåã®ãã¹ãã§ã¯ããrenderãé¢æ°ã使ç¨ãã¦Buttonã³ã³ãã¼ãã³ããä»®æ³DOMã«ã¬ã³ããªã³ã°ãããgetByTextãé¢æ°ã使ç¨ãã¦ã©ãã«ã "Click me"
ã®ãã¿ã³ãåå¾ãã¾ãã
ãã®å¾ãexpecté¢æ°ã使ç¨ãã¦ãbuttonãããã¥ã¡ã³ãå ã«åå¨ãããã©ããã確èªãã¾ãã
ãtoBeInTheDocumentãé¢æ°ã¯ãè¦ç´ ãããã¥ã¡ã³ãå ã«åå¨ãããã©ããã確èªããããã«ä½¿ç¨ããã¾ãã
2çªç®ã®ãã¹ãã§ã¯ãonClickãã³ãã©ãæ£ããå¼ã³åºããããã©ããã確èªããããã«ããfireEvent.clickãé¢æ°ã使ç¨ãã¦ãã¿ã³ãã¯ãªãã¯ãã¾ãã
Jestã®mocké¢æ°ã§ãããjest.fnãã使ç¨ãã¦ãonClickãã³ãã©ãå¼ã³åºããããã©ããã確èªãã¾ãã
ãtoHaveBeenCalledTimesãé¢æ°ã使ç¨ãã¦ãonClické¢æ°ã1åã ãå¼ã³åºããããã¨ã確èªãã¾ãã
æå¾ã«ããnpx jestãã³ãã³ãã使ç¨ãã¦ããã¹ããå®è¡ãã¾ãã
npx jest
以ä¸ããJestãã¬ã¼ã ã¯ã¼ã¯ã¨RTLã©ã¤ãã©ãªã使ç¨ãã¦Reactã¢ããªãã¦ããããã¹ãããæ¹æ³ã®åºæ¬çãªåºç¤ããå¿ç¨ã¾ã§ã®ä¾ã§ãã
çµåãã¹ãã®æ§ç¯
çµåãã¹ãã§ã¯ãè¤æ°ã®ã³ã³ãã¼ãã³ããæ©è½ãå«ãã¢ããªã±ã¼ã·ã§ã³ã®æ¯ãèãã確èªããããã«ãç°ãªãã¦ããããã¹ããçµåãã¦å®è¡ãããã¹ãæ¹æ³ã§ãã
ã§ã¯ãåè¿°ã®ã¦ããããã¹ããçµåãã¦ã¿ã¾ãããã
// IntegratedButton.js // Buttonã³ã³ãã¼ãã³ããã¤ã³ãã¼ã import Button from './Button'; // React Testing Libraryããrenderã¨fireEventãã¤ã³ãã¼ã import { render, fireEvent } from '@testing-library/react'; // çµåãã¹ã: ãã¿ã³ãæ£ããã©ãã«ãæã¡ãã¯ãªãã¯ãããæã«onClickãã³ãã©ãå¼ã³åºããããã©ããããã¹ããã test('renders a button with the correct label and calls the onClick handler when clicked', () => { // onClickãã³ãã©ãã¢ãã¯ãã const onClick = jest.fn(); // Buttonã³ã³ãã¼ãã³ãã"Click me"ã¨ããã©ãã«ã§ã¬ã³ããªã³ã°ããonClickãã³ãã©ã渡ã const { getByText } = render(<Button label="Click me" onClick={onClick} />); // ãã¿ã³ã®ã©ãã«ã"Click me"ã§ãããã¨ã確èªãã const button = getByText('Click me'); expect(button).toBeInTheDocument(); // ãã¿ã³ãã¯ãªãã¯ãã fireEvent.click(button); // onClickãã³ãã©ã1åã ãå¼ã³åºããããã¨ã確èªãã expect(onClick).toHaveBeenCalledTimes(1); });
ãã®ä¾ã§ã¯ãButtonã³ã³ãã¼ãã³ããå«ãã¢ããªã±ã¼ã·ã§ã³ã®æ¯ãèãã確èªããããã«ãã¦ããããã¹ããçµåãã¦ãã¾ãã
çµåãã¹ãã§ã¯ãButtonã³ã³ãã¼ãã³ããæ£ããã¬ã³ããªã³ã°ãããã©ãã«ãæ£ãããã¨ãã¯ãªãã¯ãããæã«onClickãã³ãã©ãå¼ã³åºããããã¨ã確èªããã¾ãã
ããã«ãããã¦ããããã¹ãããããããåºç¯ãªæ©è½ãã¹ããå®è¡ããã¾ãã
ã¦ããããã¹ãã§ã¯ãé常1ã¤ã®ã³ã³ãã¼ãã³ãã«ç¦ç¹ãå½ã¦ããã®æ¯ãèãããã¹ããã¾ãã
ä¸æ¹ãçµåãã¹ãã§ã¯ãè¤æ°ã®ã³ã³ãã¼ãã³ããçµåãããã¢ããªã±ã¼ã·ã§ã³å ¨ä½ã®æ¯ãèãã確èªãã¾ãã
çµåãã¹ãã¯ãã¢ããªã±ã¼ã·ã§ã³ãæå¾ éãã®åä½ããããã¨ã確èªããããã«éè¦ã§ããããã°ãè¦ã¤ããã®ã«å½¹ç«ã¡ã¾ãã
Jestã¨RTLã®ã¢ãã¯
Jestã使ç¨ããã¨ãReactã³ã³ãã¼ãã³ãããã¹ãããããã®ç°¡åãªæ¹æ³ãæä¾ããã¾ãã
ã¢ãã¯ã®ç®çã¯ããã¹ããããã³ã¼ããä¾ãã°ãAPIå¼ã³åºããªã©ã®å¤é¨ä¾åé¢ä¿ããåé¢ãããã¨ã§ãã
ã¤ã¾ãããã¹ãç¨ã«å®éã®ãªãã¸ã§ã¯ããç½®ãæãããã¹ãã®å¶å¾¡å¯è½ãªç¶æ ãä½ããã¨ã§ãã
以ä¸ã¯ãReactã¨Jestã使ç¨ãã¦ã¢ãã¯ãå®è£ ããä¾ã§ãã
ã¾ããJestãã¤ã³ã¹ãã¼ã«ãã¦ããäºã確èªãã¦ãã ããã
次ã«ã以ä¸ã®ãããªReactã®Counter
ã³ã³ãã¼ãã³ããããã¨ãã¾ãã
import { useState } from 'react'; const Counter = () => { const [count, setCount] = useState(0); const increment = () => { setCount(count + 1); }; return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); }; export default Counter;
ãã®ã³ã³ãã¼ãã³ãããã¹ãããã«ã¯ãuseStateããã¯ãã¢ãã¯åããå¿ è¦ãããã¾ãã
Jestã®ãjest.mockãé¢æ°ã使ç¨ãã¦ãuseStateããã¯ãã¢ãã¯åãã¾ãã
å ·ä½çã«ã¯ãuseStateãå¼ã³åºãåã«ãjest.mockããå¼ã³åºãã¦ãuseStateãã¢ãã¯åãããã¨ãã§ãã¾ãã
ã¾ããuseStateã®åæå¤ãå®ç¾©ããå¿ è¦ãããã¾ãã
以ä¸ã¯ãCounter
ã³ã³ãã¼ãã³ãããã¹ãããããã®Jestã¨RTLã®ãã¹ãã³ã¼ãã®ä¾ã¨ãªãã¾ãã
import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import Counter from './Counter'; jest.mock('react', () => ({ ...jest.requireActual('react'), useState: jest.fn(), })); describe('Counter', () => { beforeEach(() => { // ãã¹ãã®åã« useState ã®åæå¤ãè¨å®ãã useState.mockImplementation((init) => [init, jest.fn()]); }); afterEach(() => { jest.resetAllMocks(); }); it('should increment count when the button is clicked', () => { const [count, setCount] = [0, jest.fn()]; // useStateããã¯ãã¢ãã¯åãã // useStateãåæåãããæã«è¿ãå¤ãè¨å®ãã useState.mockImplementation(() => [count, setCount]); const { getByText } = render(<Counter />); const incrementButton = getByText('Increment'); fireEvent.click(incrementButton); expect(setCount).toHaveBeenCalledWith(1); }); });
ä¸è¨ã®ã³ã¼ãã§ã¯ããjest.mockãã使ç¨ãã¦ãReactã®useStateããã¯ãã¢ãã¯åãã¦ãã¾ãã
ã¾ãããbeforeEachããããã¯ã使ç¨ãã¦ãuseStateããã¯ã®åæå¤ãè¨å®ãã¦ãã¾ãã
ãã¹ãã±ã¼ã¹ã§ã¯ãuseStateããã¯ãå¼ã³åºãåã«ã¢ãã¯é¢æ°ãå®ç¾©ããuseStateãåæåãããã¨ãã«ãã®ã¢ãã¯é¢æ°ãè¿ãããããã«è¨å®ãã¦ãã¾ãã
ããã¦ããã¿ã³ãã¯ãªãã¯ããéã«ãuseStateã®setCount
ã1åå¼ã³åºããããã¨ã確èªãã¦ãã¾ãã
ããã«ãããCounter
ã³ã³ãã¼ãã³ãã®ãã¹ããã¢ãã¯åãããuseStateããã¯ã使ç¨ãã¦å®è¡ããã¾ãã
ã¢ãã¯ã使ç¨ãããã¨ã§ããã¹ãã®å¶èª²ãã³ã³ããã¼ã«ãããã¨ãã§ãããã¹ãã®é度ãåä¸ããããã¨ãã§ãã¾ãã
ã¾ããä¾åé¢ä¿ãæã¤ã³ã¼ãããã¹ããããã¨ãã«ããã®ä¾åé¢ä¿ã®ããã«ãã¹ãã失æãããã¨ããããããã¢ãã¯ã使ç¨ãã¦ä¾åé¢ä¿ãåãé¤ããã¨ã§ããã¹ãã®ä¿¡é ¼æ§ãé«ãããã¨ãã§ãã¾ãã
ããããã¢ãã¯ã使ç¨ããéã«ã¯ãã¢ãã¯ãåæ¬ã®æ¯ãèãã模å£ãã¦ãããã©ããã«æ³¨æããå¿ è¦ãããã¾ãã
ã¢ãã¯ãåæ¬ã®æ¯ãèããæ£ç¢ºã«åç¾ãã¦ããªãå ´åãæ¬æ¥ã¯å¤±æãã¹ããã¹ããæåããããæ¬æ¥ã¯æåãã¹ããã¹ãã失æãããããå¯è½æ§ãããã¾ãã
ãããå½é½æ§ãå½é°æ§ã®çµæãåºããã¨ã«ãªãã¾ãã
ä¾ãã°ãã¢ãã¯ãæ£ããå¤ãè¿ããã«å¸¸ã«åãå¤ãè¿ãããã«ãªã£ã¦ããå ´åããã¹ããæåãã¦ãã¾ãå¯è½æ§ãããã¾ãã
ã¾ããã¢ãã¯ãæ¬æ¥ã®å¦çã¨ç°ãªãåä½ãããå ´åããã¹ãã失æãã¦ãã¾ãå¯è½æ§ãããã¾ãã
ãã®ãããªãç¶æ³ãé¿ããããã«ã¯ãã¢ãã¯ãåæ¬ã®æ¯ãèããæ£ç¢ºã«åç¾ããããã«è¨å®ãããã¨ãéè¦ã§ãã
以ä¸ããReact Testing Libraryï¼RTLï¼ã¨Jestã使ç¨ãã¦ãã¢ãã¯ãä½æããæ¹æ³ã¨ãªãã¾ãã
ã¹ãããã·ã§ããã®ãã¹ã
ã¹ãããã·ã§ãããã¹ãã¯ãUIãäºæããå¤æ´ãããªããã¨ã確èªããå ´åã«å½¹ç«ã¡ã¾ãã
å ¸åçãªã¹ãããã·ã§ãããã¹ãã±ã¼ã¹ã¯ãUIã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ããã¹ãããã·ã§ãããåå¾ãã¦ãä¿åããã¦ããåç §ã¹ãããã·ã§ãããã¡ã¤ã«ã¨æ¯è¼ãã¾ãã
2ã¤ã®ã¹ãããã·ã§ãããä¸è´ããå ´åããã¹ãã¯æåãã¾ãã
2ã¤ã®ã¹ãããã·ã§ãããä¸è´ããªãå ´åã¯ãäºæããªãå¤æ´ãåå ã§ããããåç §ã¹ãããã·ã§ãããæ°ãããã¼ã¸ã§ã³ã®UIã³ã³ãã¼ãã³ãã«æ´æ°ããå¿ è¦ããããã¨ãåå ã§ããå¯è½æ§ãããã¾ãã
åææ¡ä»¶ã¨ãã¦ãã¹ãããã·ã§ãããã¹ããä½æããã«ã¯ãReactã³ã³ãã¼ãã³ããç´ç²ãªJavaScriptãªãã¸ã§ã¯ãã«ã¬ã³ããªã³ã°ã§ããããã«ããããã«ããreact-test-rendererãã©ã¤ãã©ãªãã¤ã³ã¹ãã¼ã«ããå¿ è¦ãããã¾ãã
ã¤ã³ã¹ãã¼ã«ã³ãã³ãã¯æ¬¡ã®ã¨ããã§ãã
npm i [email protected]
次ã«ãã¹ãããã·ã§ãããã¹ããä½æããããã«å¿ è¦ãªãã¡ã¤ã«ãã¤ã³ãã¼ããã¾ãã
以ä¸ã®ã³ã¼ãã§ã¯ã ãaxiosãã¨ãAppãã³ã³ãã¼ãã³ããã¤ã³ãã¼ããã¦ãã¾ãã
import axios from "axios"; import renderer from "react-test-renderer"; import App from "./App";
次ã«ãApp.jsãã¡ã¤ã«ãç·¨éãã¦ã¹ãããã·ã§ãããã¹ããå«ãã¾ãããã
// App.js import { useEffect, useState } from "react"; import axios from "axios"; function App() { // usersã¹ãã¼ããå®ç¾©ããåæå¤ã空ã®é åã«è¨å®ãã const [users, setUsers] = useState([]); useEffect(() => { async function getUsers() { // éåæå¦çãè¡ãé¢æ°ãå®ç¾©ãã try { const response = await axios.get(`https://jsonplaceholder.typicode.com/users?name=${users}`); setUsers(response.data); } catch (error) { console.error(error); } } getUsers(); // éåæå¦çãå®è¡ããé¢æ°ãå¼ã³åºã }, [users]); // usersãå¤æ´ãããã¨ãã«å度ãã§ãããã return ( <div> {/* ã¦ã¼ã¶ã¼ãªã¹ãã®ã¿ã¤ãã« */} <h1>User List</h1> <ul> {/* usersãããããã¦ãã¦ã¼ã¶ã¼åã表示ãã */} {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }
ä¸è¨ã®ã³ã¼ãã¯ãåç´ãªUser List
ã表示ããReactã³ã³ãã¼ãã³ãã§ãã
ã¾ããuseStateããã¯ã使ç¨ãã¦ãusersã®ç¶æ ãå®ç¾©ããåæå¤ã空ã®é åã«è¨å®ãã¦ãã¾ãã
useEffectããã¯ã使ç¨ãã¦ãã³ã³ãã¼ãã³ãããã¦ã³ããããã¨ãã«Axiosã使ç¨ãã¦ã¦ã¼ã¶ã¼ãã¼ã¿ãåå¾ãã¦ãã¾ãã
useEffectããã¯ã®ç¬¬2å¼æ°ã«usersã渡ãã¦ãããããusersãå¤æ´ãããã¨ãã ããå度Axiosã使ç¨ãã¦ã¦ã¼ã¶ã¼ãã¼ã¿ãåå¾ãããã¨ãã§ãã¾ãã
åå¾ããã¦ã¼ã¶ã¼ãã¼ã¿ãsetUsersã使ç¨ãã¦ãusersã®ç¶æ ã«ã»ãããã¾ãã
usersãããããã¦ãã¦ã¼ã¶ã¼åããªã¹ãå½¢å¼ã§è¡¨ç¤ºãã¦ãã¾ãã
ãã®ã³ã³ãã¼ãã³ãã¯ãã¹ãããã·ã§ãããã¹ãã§ä½¿ç¨å¯è½ã§ãã
ãã®Appã³ã³ãã¼ãã³ãã®å®éã®åä½ããã¹ãããããã«ã¯ãéåæå¦çãå®äºãã¦ããã¬ã³ããªã³ã°ãè¡ãå¿ è¦ãããã¾ãã
ãã®ãããéåæå¦çãå®äºããã¾ã§å¾ ã¤ããã«ã¯ããã¹ãã±ã¼ã¹ãéåæé¢æ°ã«ãã¦ã await ãã¼ã¯ã¼ãã使ã£ã¦éåæå¦çãå¾ ã¤å¿ è¦ãããã¾ãã
以ä¸ã®ä¾ã§ã¯ã Appã³ã³ãã¼ãã³ãã®ã¹ãããã·ã§ãããã¹ããä½æãã¦ãã¾ãã
import renderer from "react-test-renderer"; import axios from "axios"; import App from "./App"; // Appã³ã³ãã¼ãã³ããã¤ã³ãã¼ã // Appã³ã³ãã¼ãã³ãã®éåæå¦çãã¢ãã¯ãã jest.mock("axios"); // ã¹ãããã·ã§ãããã¹ããå®è¡ãããã¹ãã¹ã¤ã¼ã describe("App component snapshot", () => { // ã¹ãããã·ã§ãããã¹ãã®ãã¹ãã±ã¼ã¹ test("renders correctly", async () => { // ã¢ãã¯ã§è¿ãå½ã®ãã¼ã¿ const data = [ { id: 1, name: "John" }, { id: 2, name: "Jane" }, ]; // axios.getãå¼ã°ããã¨ãã«ãå½ã®ãã¼ã¿ãè¿ãããã«ãã axios.get.mockResolvedValue({ data }); // Appã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ããã¹ãããã·ã§ãããåå¾ const tree = renderer.create(<App />); // Appã³ã³ãã¼ãã³ãã®éåæå¦çãå¾ ã¤ await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); // ã¹ãããã·ã§ãããæ¤è¨¼ãã expect(tree.toJSON()).toMatchSnapshot(); }); });
ä¸è¨ã³ã¼ãã¯ãReactã¢ããªã±ã¼ã·ã§ã³ã®Appã³ã³ãã¼ãã³ãã«å¯¾ãã¦ãã¹ãããã·ã§ãããã¹ããè¡ããã®ã§ãã
ãã¹ãåã«ã¯ãaxios.get()ã¡ã½ãããã¢ãã¯ããå½ã®ãã¼ã¿ãè¿ãããã«è¨å®ãã¦ãã¾ãã
ãã¹ãã§ã¯ãAppã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ããã¬ã³ããªã³ã°çµæã®ã¹ãããã·ã§ãããåå¾ãã¾ãã
ãã®ã¹ãããã·ã§ãããã以åã«ä¿åããã¹ãããã·ã§ããã¨æ¯è¼ãã¦ãä¸è´ãã¦ãããã©ãããæ¤è¨¼ãã¾ãã
ãã®ãã¹ãã¯ãéåæã§ãã¼ã¿ãåå¾ããå ´åã§ããã¬ã³ããªã³ã°çµæãã¹ãããã·ã§ããã¨ãã¦åå¾ãããã¨ãã§ãã¾ãã
ãã ããéåæå¦çãè¡ãå ´åã¯ãé©åãªãã¹ãæ¹æ³ãé¸æããå¿ è¦ãããã¾ãã
ããã¦ã ãexpectãé¢æ°ã使ç¨ãã¦ãã¹ãããã·ã§ããã¨ãã¦ä¿åããã以åã®ã³ã³ãã¼ãã³ãã®ç¶æ ã¨æ¯è¼ãã¾ãã
ãã®ã¹ãããã·ã§ããã¨æ¯è¼ãããã¨ã§ã以åã®ã³ã³ãã¼ãã³ãã®ç¶æ ãå¤æ´ããã¦ããªããã©ããã確èªãããã¨ãã§ãã¾ãã
ãã以åã®ç¶æ ã¨ç°ãªãå ´åããã¹ãã¯å¤±æãã¾ãã
以ä¸ã¯ãã¹ãããã·ã§ãããç°ãªãå ´åã®ãã¹ãã失æããä¾ã§ãã
import renderer from "react-test-renderer"; import axios from "axios"; import App from "./App"; describe("App component snapshot", () => { beforeEach(() => { axios.get = jest.fn(() => Promise.resolve({ data: [ { id: 1, name: "John" }, { id: 2, name: "Jane" }, ], }) ); }); test("renders correctly", async () => { // Appã³ã³ãã¼ãã³ããã¬ã³ããªã³ã°ããã¹ãããã·ã§ãããåå¾ const tree = renderer.create(<App />).toJSON(); // ã¹ãããã·ã§ãããæ¤è¨¼ãã expect(tree).toMatchSnapshot(); // ããã§ã¹ãããã·ã§ãããåå¾ãã以åã«ä¿åãããã¹ãããã·ã§ããã¨æ¯è¼ãã // ã¹ãããã·ã§ãããç°ãªãå ´åã¯ããã¹ãã失æãã¾ã }); });
ä¾ãã°ãAppã³ã³ãã¼ãã³ãã«è¡¨ç¤ºãããã¼ã¿ãå¤æ´ãããå ´åã以åã®ã¹ãããã·ã§ããã¨ç°ãªãã¹ãããã·ã§ãããåå¾ããã¾ãã
ãã®ããããã¹ãã失æãã¾ãã
ã¹ãããã·ã§ããã®å·®ç°ã¯ãã¿ã¼ããã«ã«è¡¨ç¤ºããã¾ãã
å·®ç°ãæ£ããå ´åã¯ãã¹ãããã·ã§ãããæ´æ°ãããã¨ã§ãã¹ããééããããã¨ãã§ãã¾ãã
ã¾ãããã¹ãããã¾ããããªãå ´åã¯ãã¹ãããã·ã§ããã«å«ã¾ããæ å ±ã確èªãããã¨ã§ãåé¡ã®ç®æãç¹å®ãããã¨ãã§ãã¾ãã
ããã«ãã³ã³ãã¼ãã³ããã¯ã©ãã·ã¥ããªããã¨ã確èªããããã«ããã1ã¤ã®ãã¹ãã追å ãããã¨ãã§ãã¾ãã
ãã®ãã¹ãã¯ãReactDOM.renderã¡ã½ããã使ç¨ãã¦ãã³ã³ãã¼ãã³ããDOMã«æç»ãããReactDOM.unmountComponentAtNodeãã¡ã½ããã使ç¨ãã¦DOMããã³ã³ãã¼ãã³ããã¢ã³ãã¦ã³ããã¾ãã
ä¾ãã°ã以ä¸ã®ãããªãã¹ãã³ã¼ããæ¸ããã¨ãã§ãã¾ãã
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(<App />, div); ReactDOM.unmountComponentAtNode(div); }); it('renders correctly', () => { const tree = renderer .create(<App />) .toJSON(); expect(tree).toMatchSnapshot(); });
æåã®ãã¹ãã¯ãã³ã³ãã¼ãã³ããã¯ã©ãã·ã¥ããªããã¨ã確èªãã¾ãã
ãã®ãã¹ãã¯ãåè¿°ããããã«ReactDOM.renderã¡ã½ããã使ç¨ãã¦ãã³ã³ãã¼ãã³ããDOMã«æç»ãããReactDOM.unmountComponentAtNodeãã¡ã½ããã使ç¨ãã¦DOMããã³ã³ãã¼ãã³ããã¢ã³ãã¦ã³ããã¾ãã
2çªç®ã®ãã¹ãã¯ãã¹ãããã·ã§ãããã¹ããè¡ãã¾ãã
ååã®å®è¡æã«Jestã¯ã__snapshots__
ãã©ã«ãã«ã¹ãããã·ã§ãããã¡ã¤ã«ãèªåçã«ä½æãã¾ãã
ãã®ãã¡ã¤ã«ã«ã¯ãã¬ã³ããªã³ã°ãããã³ã³ãã¼ãã³ãã®åºåãå«ã¾ãã¾ãã
ã³ã³ãã¼ãã³ããå¤æ´ããã¨ãã¹ãããã·ã§ãããã¡ã¤ã«ãèªåçã«æ´æ°ããã¾ãã
æ´æ°ããããã¡ã¤ã«ã確èªããå¤æ´å 容ãæå³ãããã®ã§ãããã¨ã確èªããå¿ è¦ãããã¾ãã
Jestããwatch modeãã«ãªã£ã¦ããå ´åãw
ãæ¼ãã¦è©³ç´°ã表示ããu
ãæ¼ãã¦ã¹ãããã·ã§ãããæ´æ°ãããã¨ãã§ãã¾ãã
ããã«ããããã¹ãããã¹ããããã¨ãã§ããJestã«ã³ã³ãã¼ãã³ãã®æå³çãªå¤æ´ãéç¥ãããã¨ãã§ãã¾ãã
æå¾ã«
Reactã¢ããªã±ã¼ã·ã§ã³ã®ãã¹ãã¯ãé«å質ã®ã¢ããªãä½ãããã®éµã§ããããã¹ãã©ã¤ãã©ãªãJestã使ç¨ãã¦ãè¤æ°ã®ç¨®é¡ã®ãã¹ããå®è¡ã§ããããã¾ã§ä»¥ä¸ã«å®¹æã«ãªãã¾ããã
ãããã¯ããã¹ããå ç¢ãã¤å¹æçã«ããããã«å½¹ç«ã¡ã¾ãã
æ¬æ¥ã¯ä»¥ä¸ã¨ãªãã¾ãã
æå¾ã¾ã§èªãã§é ããããã¨ããããã¾ãã
ãã®è¨äºãå½¹ã«ç«ã£ãããããã¯ãã¼ã¯ãä»ã®æ¹ã«ãå ±æãã¦é ããã¨å¹¸ãã§ãã