# Javaèªå¨åæµè¯é¢è¯é¢
ðæµè¯å¼åè®ç»è¥ (opens new window)ï¼é对é¢è¯æ±èçæµè¯å¼åè®ç»è¥ï¼å¤§åå¯¼å¸ 1v1 ç§æï¼å¸®å©å¦åä» 0 å° 1 æ¿å°æµå¼æ»¡æç offer
- â ç´ææè¯¾+宿¶äºå¨ççï¼è¯¾ç¨å°å°±ä¸è´¯ç©¿ä¼ä¸çº§æ¡ä¾ï¼ç±æµè¯å¼å导å¸å ¨ç¨ä¸»è®²ï¼ç»æ å ¶ä»å©çèå¸ä»£è¯¾ã
- â 大念è¯å¼å导å¸ä¸å¯¹ä¸æ±èéªè·ï¼è¦ç课ç¨çç+ç®åæç£¨+é¢è¯æ¨¡æé¢è¯+é¢è¯å¤ççæ±èè¾ å¯¼
# ä¸ãæ¥å£èªå¨åï¼é«ä¼ï¼
# 1. Javaæ¥å£èªå¨åæ¡æ¶æ¯å¦ä½æå»ºçï¼ç¨äºåªäºææ¯æ ï¼
çæ¡è¯æ¯ï¼ æä»¬çJavaæ¥å£èªå¨åæ¡æ¶ä¸»è¦ä½¿ç¨RestAssured+TestNG+Mavenæå»ºãRestAssuredç¨æ¥åèµ·HTTPæ¥å£è¯·æ±ï¼æ¯æåç§è¯·æ±æ¹å¼ï¼TestNGä½ä¸ºæµè¯æ¡æ¶ç®¡çç¨ä¾ï¼æ¯æç¨ä¾åç»ãä¾èµãå¹¶åæ§è¡ï¼Mavenåä¾èµç®¡çåæå»ºå·¥å ·ï¼Jacksonç¨æ¥å¤çJSONæ°æ®çåºåååååºååï¼Allureçæå¯è§åæµè¯æ¥åï¼Log4j2æå°æ§è¡æ¥å¿ï¼MyBatisæä½MySQLæ°æ®åºåæ°æ®æ ¡éªãæ´ä¸ªæ¡æ¶éç¨åå±è®¾è®¡ï¼å æ¬æµè¯ç¨ä¾å±ãä¸å¡å°è£ å±ãå·¥å ·ç±»å±åé ç½®å±ï¼ä»£ç ç»ææ¸ æ°ï¼ä¾¿äºç»´æ¤ã
# 2. Javaæ¥å£èªå¨åç¸æ¯Pythonæä»ä¹ä¼å¿ï¼
çæ¡è¯æ¯ï¼ ä¸»è¦æä¸ä¸ªä¼å¿ï¼ç¬¬ä¸æ¯ç±»åå®å ¨ï¼Javaæ¯å¼ºç±»åè¯è¨ï¼ç¼è¯æå°±è½åç°ç±»åé误ï¼ä»£ç æ´å¥å£®ï¼ç¬¬äºæ¯æ§è½æ´å¥½ï¼Javaæ§è¡æçæ¯Pythoné«ï¼éåå¤§è§æ¨¡å¹¶åæµè¯ï¼ç¬¬ä¸æ¯ä¼ä¸çº§æ¯ææ´æçï¼å¾å¤æµè¯å¹³å°åCI/CDå·¥å ·é½æ¯Javaææ¯æ ï¼éææ´æ¹ä¾¿ãæä»¬çµåé¡¹ç®æä¸ç¾ä¸ªæ¥å£ï¼ç¨Javaæ¡æ¶æ§è¡500æ¡ç¨ä¾åªé5åéï¼ç¨³å®æ§ä¹å¾å¥½ã
# 3. RestAssuredåHttpClientæä»ä¹åºå«ï¼ä¸ºä»ä¹éRestAssuredï¼
çæ¡è¯æ¯ï¼ HttpClientæ¯ApacheçHTTP客æ·ç«¯åºï¼åè½å¼ºå¤§ä½API夿ï¼éè¦åå¾å¤ä»£ç ï¼RestAssuredä¸é¨ä¸ºREST APIæµè¯è®¾è®¡ï¼APIç®æ´ï¼æ¯æBDD飿 ¼ï¼å¯è¯»æ§å¼ºãæ¯å¦åéä¸ä¸ªPOST请æ±ï¼HttpClientè¦å10å¤è¡ä»£ç ï¼RestAssuredåªéè¦3-4è¡ãèä¸RestAssuredå ç½®JSON/XMLè§£æã请æ±ååºæ¥å¿ãæè¨çåè½ï¼å¼ç®±å³ç¨ãæä»¥æä»¬éæ©RestAssuredï¼å¼åæçæ´é«ã
# 4. TestNGåJUnitçåºå«æ¯ä»ä¹ï¼ä¸ºä»ä¹ç¨TestNGï¼
çæ¡è¯æ¯ï¼ 主è¦åºå«æä¸ç¹ï¼ç¬¬ä¸ï¼TestNGæ¯ææ´çµæ´»çç¨ä¾ç»ç»ï¼å¯ä»¥ç¨groupsåç»ãdependsOnMethods设置ä¾èµï¼ç¬¬äºï¼TestNGçæ³¨è§£æ´ä¸°å¯ï¼æ¯å¦@DataProvideråæ°æ®é©±å¨ï¼@Parametersä¼ åï¼ç¬¬ä¸ï¼TestNGæ¯æå¹¶åæ§è¡å失败éè¯ãæä»¬çµå项ç®ç¨ä¾å¾å¤ï¼ç¨TestNGçgroupsæä¸åãæ¯ä»ã鿬¾çä¸å模ååç»ï¼å¯ä»¥çµæ´»æ§è¡ï¼èä¸å¤±è´¥éè¯æºå¶è½æé¤ç¯å¢å¹²æ°ï¼æä»¥éæ©TestNGã
# 5. å¦ä½å®ç°æ¥å£å ³èï¼ä»è®¢åæ¥å£æå订åå·ä¼ ç»æ¯ä»æ¥å£ï¼
çæ¡è¯æ¯ï¼
æä»¬ç¨JsonPathæåååºæ°æ®ãæ¯å¦ä¸åæ¥å£è¿å订åå·ï¼æç¨JsonPath表达å¼$.data.orderIdæååºæ¥ï¼åå°å
¨å±åéæè
ç±»çéæåééï¼ç¶åæ¯ä»æ¥å£ç´æ¥å¼ç¨è¿ä¸ªåéã妿æ¥å£å
³èæ¯è¾å¤æï¼æä¼å°è£
ä¸ä¸ªContextç±»ç»ä¸ç®¡çå
³èæ°æ®ï¼æ¯ä¸ªæ¥å£æ§è¡å®æéè¦çæ°æ®putè¿å»ï¼ä¸ä¸ªæ¥å£getåºæ¥ä½¿ç¨ï¼è¿æ ·ä»£ç æ´æ¸
æ°ã
# 6. æ°æ®é©±å¨æä¹å®ç°ï¼ç¨Excelè¿æ¯ä»£ç ï¼
çæ¡è¯æ¯ï¼ æä»¬ä¸»è¦ç¨TestNGç@DataProvider注解å®ç°æ°æ®é©±å¨ã对äºç®ååºæ¯ï¼ç´æ¥å¨ä»£ç éè¿åObject[][]æ°ç»ï¼å¯¹äºå¤æåºæ¯ï¼æä»¬ç¨POIåºè¯»åExcelæä»¶ï¼ææ¯è¡æ°æ®å°è£ æJavaBean对象ï¼ç¶åç¨@DataProviderè¿åãè¿ç§æ¹å¼çµæ´»æ§é«ï¼æµè¯æ°æ®å代ç å离ï¼ä¸å¡äººåä¹è½ç»´æ¤Excelæ°æ®ãæä»¬çµåçååæ¥è¯¢æ¥å£ï¼å°±æ¯ç¨Excel管çå åç»æµè¯æ°æ®ï¼ä¸ä¸ªæµè¯æ¹æ³è·å®ææåºæ¯ã
# 7. æ¥å£é´æå¦ä½å¤çï¼Tokenæä¹ç®¡çï¼
çæ¡è¯æ¯ï¼
æä»¬çµå项ç®ç¨JWT Tokené´æãé¦å
è°ç¨ç»å½æ¥å£è·åTokenï¼ç¨JsonPathæååºæ¥åå°å
¨å±åéï¼ç¶åå
¶ä»æ¥å£ç请æ±å¤´ç»ä¸æ·»å Authorization: Bearer {token}ãæå°è£
äºä¸ä¸ªBaseTestç±»ï¼å¨@BeforeClasséè·åToken并设置å°RequestSpecificationä¸ï¼æææµè¯ç±»ç»§æ¿è¿ä¸ªç±»å°±èªå¨å¸¦ä¸TokenäºãTokenæè¿ææ¶é´ï¼æè¿å äºå¤±æèªå¨å·æ°çé»è¾ï¼ç¡®ä¿ç¨ä¾ç¨³å®æ§è¡ã
# 8. å¦ä½åæ¥å£Mockæµè¯ï¼
çæ¡è¯æ¯ï¼ æä»¬ç¨WireMock忥å£Mockãæ¯å¦ä¸åæ¥å£ä¾èµåºåæå¡ï¼ä½åºåæå¡è¿æ²¡å¼åå®ï¼æå°±ç¨WireMock卿¬å°å¯å¨ä¸ä¸ªMockæå¡å¨ï¼é ç½®è§åè¿ååºå®çåºåæ°æ®ãè¿æ ·å¯ä»¥æåæµè¯ä¸åé»è¾ï¼ä¸è¢«ä¾èµé»å¡ãMockè¿è½æ¨¡æåç§å¼å¸¸åºæ¯ï¼æ¯å¦åºåä¸è¶³ãæå¡è¶ æ¶ãè¿åé误ç çï¼è¦çæ´å ¨é¢ã
# 9. æè¨æä¹åï¼ç¨ä»ä¹å·¥å ·ï¼
çæ¡è¯æ¯ï¼
æä»¬ä¸»è¦ç¨HamcreståAssertJ两个æè¨åºãHamcrestæ¯TestNGæ¨èçï¼è¯æ³ç®æ´ï¼æ¯å¦assertThat(response.statusCode(), equalTo(200))ï¼AssertJæ´å¼ºå¤§ï¼æ¯æé¾å¼è°ç¨å丰å¯çæè¨ï¼æ¯å¦assertThat(order.getStatus()).isNotNull().isEqualTo("PAID")ãæä¸è¬ä¼ç»å使ç¨ï¼å¯¹ç¶æç ãååºåæ®µãæ°æ®åºæ°æ®é½åæè¨ï¼ç¡®ä¿æ¥å£è¿åæ£ç¡®ã
# 10. å¦ä½å¤çæ¥å£å å¯åç¾åï¼
çæ¡è¯æ¯ï¼ æä»¬çµåçæ¯ä»æ¥å£éè¦MD5ç¾åãæå°è£ äºä¸ä¸ªSignUtilså·¥å ·ç±»ï¼æè¯·æ±åæ°æåå ¸åºæåï¼æ¼æ¥åå å¯é¥åMD5ï¼çæç¾ååæ®µãç¶åå¨åé请æ±åè°ç¨è¿ä¸ªå·¥å ·ç±»ï¼èªå¨ç»è¯·æ±ä½å ä¸signåæ®µãååºè§£å¯ä¹ç±»ä¼¼ï¼æç¨Base64æAESè§£å¯å·¥å ·ç±»å¤çè¿åæ°æ®ãè¿æ ·ä¸å¡ä»£ç ä¸ç¨å ³å¿å è§£å¯ç»èï¼ä¿æç®æ´ã
# 11. å¹¶åæµè¯æä¹åï¼
çæ¡è¯æ¯ï¼
TestNGæ¯æç¨ä¾å¹¶åæ§è¡ï¼æå¨testng.xmléé
ç½®parallel="methods" thread-count="10"ï¼å°±è½10个线ç¨å¹¶åè·æµè¯æ¹æ³ãæä»¬ç¨è¿ä¸ªæµè¯ç§ææ¥å£ï¼æ¨¡æ100ä¸ªç¨æ·åæ¶ä¸åï¼éªè¯åºåæ£åæ¯å¦åç¡®ãææ²¡æè¶
åé®é¢ãè¿ä¼ç»åæ°æ®åºæ¥è¯¢ï¼ç¡®è®¤æç»åºåæ°éæ£ç¡®ãè¿ç§å¹¶åæµè¯è½æ´é²çº¿ç¨å®å
¨é®é¢ã
# 12. æµè¯æ°æ®å¦ä½åå¤åæ¸ çï¼
çæ¡è¯æ¯ï¼ æç¨@BeforeMethodå夿°æ®ï¼@AfterMethodæ¸ çæ°æ®ãæ¯å¦æµè¯è®¢åæ¥è¯¢æ¥å£ï¼æå¨@BeforeMethodéè°ç¨ä¸åæ¥å£å建æµè¯è®¢åï¼æ¿å°è®¢åå·ï¼æµè¯å®æåå¨@AfterMethodéè°ç¨åæ¶è®¢åæ¥å£æè ç´æ¥å 餿°æ®åºè®°å½ï¼ä¿è¯æ°æ®å¹²åã对äºå¤æåºæ¯ï¼æè¿ä¼ç¨MyBatisç´æ¥æä½æ°æ®åºæ¹éæå ¥æµè¯æ°æ®ï¼é度æ´å¿«ã
# 13. å¦ä½ç»è®¡ä»£ç è¦ççï¼
çæ¡è¯æ¯ï¼ æä»¬ç¨JaCoCoç»è®¡ä»£ç è¦ççãå¨Mavençpom.xmléé ç½®JaCoCoæä»¶ï¼è·å®æµè¯åçæjacoco.execæä»¶ï¼ç¶åç¨AntæMavenå½ä»¤çæHTMLæ¥åãæ¥åè½çå°æ¯ä¸ªç±»ãæ¯ä¸ªæ¹æ³çè¦ççï¼åªäºä»£ç 没被æµè¯è¦çãæä»¬è¦æ±æ ¸å¿æ¥å£ä»£ç è¦ççè¾¾å°80%以ä¸ï¼éè¿è¦ççæ¥åé对æ§è¡¥å ç¨ä¾ï¼å®ç°ç²¾åæµè¯ã
# 14. æç»éææä¹åï¼
çæ¡è¯æ¯ï¼
æä»¬ç¨Jenkinså®ç°CI/CDãå¨Jenkinséé
ç½®Maven项ç®ï¼è®¾ç½®Gitå°åï¼æ·»å æå»ºè§¦åå¨ï¼æ¯å¦ä»£ç æäº¤èªå¨æå»ºæè
æ¯å¤©æä¸å®æ¶æå»ºãæå»ºæ¥éª¤æ§è¡mvn clean testï¼è·å®èªå¨åæµè¯åï¼ç¨Allureæä»¶çææµè¯æ¥åï¼åé
ç½®é®ä»¶éç¥ææ¥ååç»ç¸å
³äººåãæ´ä¸ªæµç¨å
¨èªå¨ï¼å¼åæäº¤ä»£ç åå°±è½åæ¶åç°é®é¢ã
# 15. å¦ä½æé«æ¡æ¶çç¨³å®æ§ï¼
çæ¡è¯æ¯ï¼ 主è¦ä¸ä¸ªæ¹é¢ï¼ç¬¬ä¸æ¯å¢å 失败éè¯ï¼TestNGæ@RetryAnalyzer注解ï¼å¤±è´¥çç¨ä¾èªå¨éè·3æ¬¡ï¼æé¤ç¯å¢ä¸ç¨³å®çå¹²æ°ï¼ç¬¬äºæ¯å çå¾ æºå¶ï¼æ¥å£ååºæ ¢æ¶ç¨Thread.sleepæè 轮询çå¾ ï¼é¿å è¶ æ¶ï¼ç¬¬ä¸æ¯å¼å¸¸å¤çï¼ç¨try-catchæè·å¼å¸¸ï¼è®°å½è¯¦ç»æ¥å¿ï¼ç¨ä¾å¤±è´¥ä¹è½ç»§ç»æ§è¡ä¸å½±åå ¶ä»ç¨ä¾ãæä»¬æ¡æ¶ç»è¿è¿äºä¼åï¼ç¨³å®æ§æåå¾å¤ã
# 16. æ§è½æµè¯æ°æ®æä¹åå¤ï¼
çæ¡è¯æ¯ï¼ æç¨ä¸ç§æ¹å¼ï¼ç¬¬ä¸æ¯ä»æ°æ®åºæç°ææ°æ®ï¼å¯¼åºæCSVæJSONï¼ç¬¬äºæ¯åèªå¨åèæ¬è°ç¨æ¥å£æ¹éé æ°æ®ï¼æ¯å¦è°æ³¨åæ¥å£å建1000ä¸ªç¨æ·ï¼ç¬¬ä¸æ¯ç¨MyBatisåSQLæ¹éæå ¥æ°æ®ãçµå项ç®ååæ°æ®æç´æ¥åSQLæå ¥ï¼ç¨æ·æ°æ®ç¨èªå¨åæ¥å£å建ï¼è®¢åæ°æ®ç¨JMeteråæ°åCSVæä»¶ï¼ç»åä½¿ç¨æçæé«ã
# 17. éå°è¿åªäºææ¯é¾ç¹ï¼
çæ¡è¯æ¯ï¼ æé¾çæ¯æ¥å£ä¾èµå¤æçåºæ¯ãæ¯å¦ä¸åæµç¨ï¼è¦å æ¥åååºåï¼åå建订åï¼åè°ç¨æ¯ä»æ¥å£ï¼æåæ£ååºåæ´æ°è®¢åç¶æï¼æ¶å4-5个æ¥å£ç串èãæéè¿å°è£ ä¸å¡æµç¨ç±»ï¼æè¿äºæ¥å£æé¡ºåºå°è£ æä¸ä¸ªæ¹æ³ï¼ä¼ å ¥ååIDåç¨æ·IDå°±è½å®ææ´ä¸ªä¸åï¼ä¸å±æµè¯ç¨ä¾è°ç¨èµ·æ¥å¾ç®åãè¿æå°±æ¯ç¯å¢ä¸ç¨³å®å¯¼è´ç¨ä¾å¤±è´¥ï¼æå äºéè¯åè¯¦ç»æ¥å¿ï¼é®é¢åºæ¬è§£å³äºã
# 18. 为ä»ä¹éJavaè䏿¯Python忥å£èªå¨åï¼
çæ¡è¯æ¯ï¼ 主è¦ä¸ä¸ªåå ï¼ç¬¬ä¸ï¼æä»¬å端æ¯Javaææ¯æ ï¼æµè¯ç¨Javaè½æ´å¥½å°ç解代ç é»è¾ï¼æ¹ä¾¿åå¼åæ²éï¼ç¬¬äºï¼Javaæ§è¡æçé«ï¼500æ¡ç¨ä¾è·5åéï¼Pythonè¦10åéï¼ç¬¬ä¸ï¼å ¬å¸æJavaæµè¯å¹³å°åå·¥å ·ï¼éææ¹ä¾¿ãè½ç¶Pythonä¸æå¿«ï¼ä½ç»¼åèèJavaæ´éåæä»¬é¡¹ç®ã
# 19. å¦ä½åæ¥å£æ§è½æµè¯ï¼
çæ¡è¯æ¯ï¼ è½ç¶æä»¬ä¸»è¦ç¨JMeteråæ§è½æµè¯ï¼ä½Javaæ¥å£èªå¨åæ¡æ¶ä¹è½åç®åçæ§è½æµè¯ãæç¨å¤çº¿ç¨æ¨¡æå¹¶åï¼CountDownLatchæ§å¶çº¿ç¨åæ¶å起请æ±ï¼è®°å½ååºæ¶é´åæåçãæ¯å¦æµè¯ç§ææ¥å£ï¼æå¯å¨100个线ç¨åæ¶ä¸åï¼ç»è®¡TPSåå¹³åååºæ¶é´ï¼éªè¯æ¥å£æ§è½æ¯å¦è¾¾æ ãè¿ç§æ¹å¼éåå¿«ééªè¯ï¼æ·±åº¦æ§è½æµè¯è¿æ¯ç¨ä¸ä¸å·¥å ·ã
# 20. æ¡æ¶è¿æåªäºå¯ä»¥ä¼åçå°æ¹ï¼
çæ¡è¯æ¯ï¼ 主è¦ä¸ä¸ªæ¹åï¼ç¬¬ä¸æ¯ç¨ä¾æ§è¡é度ï¼ç®å500æ¡ç¨ä¾5åéï¼å¯ä»¥ä¼åæ3åéï¼éè¿å¢å å¹¶å度ååå°ä¸å¿ è¦ççå¾ ï¼ç¬¬äºæ¯å¤±è´¥åæï¼ç°å¨å¤±è´¥éè¦äººå·¥çæ¥å¿ï¼å¯ä»¥åæºè½åæï¼èªå¨å类失败åå ï¼ç¬¬ä¸æ¯æµè¯æ°æ®ç®¡çï¼ç°å¨æ°æ®åæ£å¨ä»£ç åExceléï¼å¯ä»¥ç»ä¸å°æ°æ®åºæè é ç½®ä¸å¿ï¼æ´å¥½ç®¡çãè¿äºä¼åè½è®©æ¡æ¶æ´é«ææç¨ã
# äºãWebèªå¨åï¼ä¸çï¼
# 1. Java Webèªå¨åæ¡æ¶æ¯å¦ä½æå»ºçï¼ç¨äºåªäºææ¯ï¼
çæ¡è¯æ¯ï¼ æä»¬ä½¿ç¨Selenium+TestNG+Mavenæå»ºWebèªå¨åæ¡æ¶ãSelenium WebDriverè´è´£æµè§å¨æä½ï¼TestNGç®¡çæµè¯ç¨ä¾åæè¨ï¼Mavenåä¾èµç®¡çåæå»ºãè¿éæäºPageFactoryå页é¢å¯¹è±¡æ¨¡åï¼ExtentReportsçææµè¯æ¥åï¼Log4j2è®°å½æ¥å¿ãæ´ä¸ªæ¡æ¶éç¨POM设计模å¼ï¼æé¡µé¢å ç´ åä¸å¡æä½å离ï¼ä¸ä¸ªé¡µé¢å¯¹åºä¸ä¸ªPageç±»ï¼æµè¯ç¨ä¾åªè°ç¨Pageç±»çæ¹æ³ï¼ä»£ç å¯ç»´æ¤æ§å¾å¼ºã
# 2. ä»ä¹æ¯POMï¼é¡µé¢å¯¹è±¡æ¨¡åï¼ï¼ä¸ºä»ä¹è¦ç¨POMï¼
çæ¡è¯æ¯ï¼
POMå°±æ¯ææ¯ä¸ªé¡µé¢çå
ç´ å®ä½åæä½å°è£
æä¸ä¸ªPageç±»ãæ¯å¦ç»å½é¡µé¢å°è£
æLoginPageç±»ï¼éé¢å®ä¹ç¨æ·åè¾å
¥æ¡ãå¯ç è¾å
¥æ¡ãç»å½æé®çå®ä½ï¼ä»¥åè¾å
¥ç¨æ·åãè¾å
¥å¯ç ãç¹å»ç»å½çæ¹æ³ãæµè¯ç¨ä¾åªéè¦è°ç¨loginPage.login(username, password)ï¼ä¸ç¨å
³å¿å
ç´ æä¹å®ä½ã好夿¯å½é¡µé¢å
ç´ ååæ¶ï¼åªéè¦ä¿®æ¹Pageç±»ï¼æµè¯ç¨ä¾ä¸ç¨å¨ï¼ç»´æ¤ææ¬å¤§å¤§éä½ãæä»¬çµåé¡¹ç®æ20å¤ä¸ªPageç±»ï¼ç»æé常æ¸
æ°ã
# 3. å¦ä½å¤çå ç´ å®ä½ï¼ä¼å 使ç¨åªç§å®ä½æ¹å¼ï¼
çæ¡è¯æ¯ï¼ å ç´ å®ä½æä¼å 使ç¨IDï¼å 为IDæ¯å¯ä¸çï¼å®ä½é度快ä¸ç¨³å®ãå¦ææ²¡æIDï¼æä¼ç¨nameæè classã对äºå¤æåºæ¯ç¨XPathæCSS Selectorï¼XPathåè½å¼ºå¤§ä½æ§è½ç¨å·®ï¼CSS Selectoræ§è½å¥½ä½åè½æéï¼æä¼æ ¹æ®å®é æ åµéæ©ãæä»¬çµå项ç®ååå表ç¨XPathå®ä½ç¬¬å 个ååï¼è´ç©è½¦ç¨CSS Selectorå®ä½ä»·æ ¼å ç´ ãå¦å¤æä¼å°½éé¿å ç¨ææ¬å®ä½ï¼å ä¸ºææ¬å®¹æååã
# 4. å¦ä½å¤ç卿å ç´ ï¼å ç´ å è½½æ ¢æä¹åï¼
çæ¡è¯æ¯ï¼
æä¸»è¦ç¨ä¸ç§çå¾
æ¹å¼ï¼ç¬¬ä¸æ¯æ¾å¼çå¾
WebDriverWaitï¼çå¾
ç¹å®å
ç´ åºç°æå¯ç¹å»ï¼æ¯å¦WebDriverWait.until(ExpectedConditions.elementToBeClickable(element))ï¼ç¬¬äºæ¯éå¼çå¾
implicitlyWaitï¼è®¾ç½®å
¨å±çå¾
æ¶é´ï¼ç¬¬ä¸æ¯å¼ºå¶çå¾
Thread.sleepï¼ä½è¿ä¸ªä¼ææ
¢æ§è¡éåº¦ï¼æå°½éå°ç¨ãæä»¬çµåçæç´¢ç»æé¡µé¢ï¼ååå表æ¯Ajaxå è½½çï¼æç¨æ¾å¼çå¾
ï¼çå¾
第ä¸ä¸ªååå
ç´ åºç°åè¿è¡åç»æä½ï¼é常稳å®ã
# 5. å¦ä½å¤çå¼¹çªãAlertãiframeï¼
çæ¡è¯æ¯ï¼
Alertå¼¹çªç¨driver.switchTo().alert()忢ï¼ç¶åaccept()æ¥åædismiss()åæ¶ãiframeç¨driver.switchTo().frame()忢è¿å»æä½ï¼æä½å®ç¨driver.switchTo().defaultContent()åå主页é¢ãæ°çªå£ç¨driver.switchTo().window(windowHandle)忢ãæä»¬çµåæ¯ä»é¡µé¢æ¯iframeåµå
¥çï¼æå°±å
忢å°iframeï¼è¾å
¥æ¯ä»å¯ç ï¼ç¹å»ç¡®è®¤ï¼ç¶ååå主页é¢éªè¯è®¢åç¶æï¼è¿äºæä½é½å°è£
å¨PaymentPageç±»éã
# 6. æµè§å¨å ¼å®¹æ§æµè¯æä¹åï¼
çæ¡è¯æ¯ï¼
æç¨TestNGç@Parametersæ³¨è§£ä¼ å
¥æµè§å¨ç±»åï¼å¨@BeforeClasséæ ¹æ®åæ°åå§åä¸åçWebDriverãå¨testng.xmléé
ç½®å¤ä¸ªtestèç¹ï¼åå«ä¼ å
¥ChromeãFirefoxãEdgeåæ°ï¼è¿æ ·ä¸å¥ç¨ä¾å°±è½å¨å¤ä¸ªæµè§å¨ä¸è·ãæä»¬çµå项ç®ä¸»è¦æµè¯ChromeåSafariï¼å ä¸ºç¨æ·éæå¤§ãå¦å¤è¿ä¼æµè¯ä¸åå辨çï¼ç¨driver.manage().window().setSize()设置çªå£å¤§å°ï¼éªè¯ååºå¼å¸å±ã
# 7. å¦ä½æªå¾ï¼ä»ä¹æ¶åæªå¾ï¼
çæ¡è¯æ¯ï¼
æç¨TakesScreenshotæ¥å£æªå¾ï¼ä»£ç æ¯File screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE)ï¼ç¶åç¨FileUtilsä¿åå°æå®è·¯å¾ãæå¨ä¸¤ä¸ªå°æ¹æªå¾ï¼ä¸æ¯ç¨ä¾å¤±è´¥æ¶ï¼å¨@AfterMethodéå¤æå¦æå¤±è´¥å°±æªå¾ï¼æ¹ä¾¿å®ä½é®é¢ï¼äºæ¯å
³é®æ¥éª¤ï¼æ¯å¦ä¸åæåãæ¯ä»å®æï¼æªå¾çè¯ãæªå¾æä»¶åæç¨æµè¯æ¹æ³å_æ¶é´æ³.pngå½åï¼å¹¶ä¸ææªå¾è·¯å¾åå°Allureæ¥åéï¼æ¥çæ¥åæ¶è½ç´æ¥çå°æªå¾ã
# 8. æ°æ®é©±å¨æä¹å®ç°ï¼
çæ¡è¯æ¯ï¼ æç¨TestNGç@DataProvider注解å®ç°æ°æ®é©±å¨ã对äºç®ååºæ¯ï¼ç´æ¥å¨ä»£ç éè¿åäºç»´æ°ç»ï¼å¯¹äºå¤æåºæ¯ï¼ç¨POI读åExcelï¼ææ¯è¡æ°æ®è½¬æå¯¹è±¡è¿åãæ¯å¦æµè¯ååæç´¢åè½ï¼æå¨Exceléç»´æ¤ä¸åçæç´¢å ³é®è¯åææç»æï¼@DataProvider读ååä¼ ç»æµè¯æ¹æ³ï¼ä¸ä¸ªæ¹æ³å°±è½è·å®ææåºæ¯ãè¿æ ·æµè¯æ°æ®å代ç å离ï¼ä¸å¡äººåä¹è½ç»´æ¤Excelï¼éå¸¸çµæ´»ã
# 9. å¦ä½å¤çéªè¯ç ï¼
çæ¡è¯æ¯ï¼ éªè¯ç 主è¦ä¸ç§å¤çæ¹å¼ï¼ç¬¬ä¸æ¯è®©å¼åæä¾æµè¯ç¯å¢çä¸è½éªè¯ç ï¼æ¯å¦åºå®è¾å ¥"1234"å°±è½éè¿ï¼ç¬¬äºæ¯å¼åæä¾è·³è¿éªè¯ç çåé¨ï¼æµè¯è´¦å·ä¸æ ¡éªéªè¯ç ï¼ç¬¬ä¸æ¯ç¨OCRè¯å«ï¼ä½åç¡®çä¸é«æä»¬å¾å°ç¨ãæä»¬çµåé¡¹ç®æµè¯ç¯å¢ç¨çæ¯ä¸è½éªè¯ç æ¹å¼ï¼å¨é ç½®æä»¶éé ç½®éªè¯ç å¼ï¼ä»£ç éç´æ¥è¯»åå¡«å ¥ï¼ç®å稳å®ãç产ç¯å¢æµè¯æä»¬ä¼æå·¥ä»å ¥è¾å ¥éªè¯ç ã
# 10. å¦ä½åæè¨ï¼æè¨å¤±è´¥åæ¯å¦ç»§ç»æ§è¡ï¼
çæ¡è¯æ¯ï¼
æä¸»è¦ç¨TestNGçAssertç±»åæè¨ï¼æ¯å¦Assert.assertEquals(actual, expected)ãé»è®¤æè¨å¤±è´¥å伿å¼å¸¸ï¼åé¢ç代ç 䏿§è¡ãå¦æææ³å¤ä¸ªæè¨å¤±è´¥åç»§ç»æ§è¡ï¼æç¨SoftAssertï¼å®ä¼æ¶éææå¤±è´¥çæè¨ï¼æåè°ç¨assertAll()ç»ä¸æåºãæ¯å¦éªè¯è®¢å详æ
é¡µï¼æä¼æè¨è®¢åå·ãåååç§°ãä»·æ ¼ãç¶æçå¤ä¸ªå段ï¼ç¨SoftAssertå¯ä»¥ä¸æ¬¡çå°ææå¤±è´¥çæè¨ï¼å®ä½é®é¢æ´å¿«ã
# 11. PageFactoryåæ®éPageç±»æä»ä¹åºå«ï¼
çæ¡è¯æ¯ï¼
PageFactoryæ¯Seleniumæä¾ç页é¢å·¥å模å¼ãæ®éPageç±»ç¨driver.findElement(By.id("username"))æ¥æ¾å
ç´ ï¼æ¯æ¬¡é½è¦åå®ä½ä»£ç ï¼PageFactoryç¨@FindBy(id="username")æ³¨è§£ç´æ¥å£°æå
ç´ ï¼å¨æé 彿°éè°ç¨PageFactory.initElements(driver, this)åå§åï¼ä»£ç æ´ç®æ´ãèä¸PageFactoryæ¯ææå è½½ï¼å
ç´ åªå¨ä½¿ç¨æ¶ææ¥æ¾ï¼æ§è½æ´å¥½ãæä»¬çµå项ç®ç»ä¸ç¨PageFactoryï¼ä»£ç éå°äº30%ã
# 12. å¦ä½æ§è¡JavaScript代ç ï¼
çæ¡è¯æ¯ï¼
æç¨JavascriptExecutoræ¥å£æ§è¡JS代ç ã常è§åºæ¯æä¸ä¸ªï¼ä¸æ¯æ»å¨é¡µé¢ï¼js.executeScript("window.scrollTo(0, document.body.scrollHeight)")æ»å¨å°åºé¨ï¼è§¦åæå è½½ï¼äºæ¯æä½éèå
ç´ ï¼æäºå
ç´ Seleniumç¹ä¸äºï¼ç¨JSç´æ¥clickï¼ä¸æ¯è·åå
ç´ å±æ§ï¼æ¯å¦js.executeScript("return arguments[0].value", element)è·åinputçvalueãæä»¬çµåçåååè¡¨æ¯æ éæ»å¨çï¼æå°±ç¨JSæ»å¨å°åºé¨å è½½æ´å¤ååã
# 13. å¦ä½å¤çæä»¶ä¸ä¼ åä¸è½½ï¼
çæ¡è¯æ¯ï¼ æä»¶ä¸ä¼ æä¸¤ç§æ¹å¼ï¼ä¸æ¯å¦ææ¯inputæ ç¾type="file"ï¼ç´æ¥ç¨sendKeys()ä¼ å ¥æä»¶è·¯å¾ï¼äºæ¯å¦ææ¯èªå®ä¹ä¸ä¼ æé®ï¼éè¦ç¨AutoItæRobot类模æé®çæä½ï¼ä½è¿ç§æ¹å¼ä¸æ¨èå 为ä¸ç¨³å®ãæä»¶ä¸è½½æä¸è¬ä¸éªè¯ä¸è½½è¿ç¨ï¼èæ¯éªè¯ä¸è½½åçæä»¶ï¼ç¨JavaçFileç±»æ£æ¥ä¸è½½ç®å½æ¯å¦æå¯¹åºæä»¶ï¼æè éªè¯æä»¶å¤§å°ãå 容æ¯å¦æ£ç¡®ãæä»¬çµåç订å导åºåè½ï¼æå°±éªè¯å¯¼åºçExcelæä»¶æ¯å¦åå¨ä¸ææ°æ®ã
# 14. å¤çªå£ãå¤Tab页æä¹å¤çï¼
çæ¡è¯æ¯ï¼
æç¨driver.getWindowHandles()è·åææçªå£å¥æï¼ç¶åç¨driver.switchTo().window(handle)忢ãä¸è¬æä¼å¨æå¼æ°çªå£åå
ä¿ååçªå£å¥æï¼æä½å®æ°çªå£ååååçªå£ãæ¯å¦åå详æ
页ç¹å»æ¥çç©æµï¼ä¼æ°å¼ä¸ä¸ªTabé¡µï¼æå°±è·åæ°Tabçhandleï¼åè¿å»éªè¯ç©æµä¿¡æ¯ï¼ç¶åå
³éè¿ä¸ªTabï¼åååå详æ
页ãæå°è£
äºä¸ä¸ªWindowUtilså·¥å
·ç±»ç»ä¸ç®¡ççªå£åæ¢ï¼ä»£ç æ´æ¸
æ°ã
# 15. å¦ä½å¤ç坿æ¬ç¼è¾å¨ï¼
çæ¡è¯æ¯ï¼
坿æ¬ç¼è¾å¨ä¸è¬æ¯iframeæè
contenteditableçdivã妿æ¯iframeï¼æå
switchTo().frame()忢è¿å»ï¼ç¶åç¨sendKeys()è¾å
¥å
容ï¼å¦ææ¯divï¼æç¨JavascriptExecutoræ§è¡arguments[0].innerHTML='content'ç´æ¥è®¾ç½®å
容ï¼å 为sendKeys()ææ¶åä¸ç¨³å®ãæä»¬çµåçååè¯ä»·æ¯å¯ææ¬ç¼è¾å¨ï¼æå°±ç¨JSç´æ¥è®¾ç½®innerHTMLï¼ç¶åæäº¤è¯ä»·ï¼éªè¯è¯ä»·å
容æ¯å¦æ£ç¡®æ¾ç¤ºã
# 16. æµè¯æ°æ®å¦ä½ç®¡çï¼
çæ¡è¯æ¯ï¼
æä»¬ææµè¯æ°æ®åä¸ç±»ç®¡çï¼ç¬¬ä¸ç±»æ¯é
置信æ¯ï¼æ¯å¦URLãè´¦å·å¯ç ï¼æ¾å¨propertiesæä»¶éï¼ç¨ResourceBundle读åï¼ç¬¬äºç±»æ¯ä¸å¡æ°æ®ï¼æ¯å¦ååä¿¡æ¯ãç¨æ·ä¿¡æ¯ï¼æ¾å¨ExcelæJSONæä»¶éï¼ç¨POIæJackson读åï¼ç¬¬ä¸ç±»æ¯æ°æ®åºæ°æ®ï¼ç´æ¥ç¨JDBCæMyBatisæä½æ°æ®åºãæå°è£
äºä¸ä¸ªDataUtilså·¥å
·ç±»ç»ä¸è¯»åï¼æµè¯ç¨ä¾åªéè¦è°ç¨DataUtils.getTestData("login")å°±è½è·åç»å½æ°æ®ï¼é常æ¹ä¾¿ã
# 17. 失败éè¯æºå¶æä¹å®ç°ï¼
çæ¡è¯æ¯ï¼
æå®ç°äºä¸ä¸ªRetryAnalyzerç±»ï¼ç»§æ¿IRetryAnalyzeræ¥å£ï¼å¨retryæ¹æ³éå¤æå¦æå¤±è´¥ä¸éè¯æ¬¡æ°å°äº3次就è¿åtrueï¼TestNGä¼èªå¨éè·å¤±è´¥çç¨ä¾ãç¶åå¨testng.xmléé
ç½®<listeners><listener class-name="com.test.RetryAnalyzer"/></listeners>å
¨å±çæï¼æè
卿µè¯æ¹æ³ä¸å @Test(retryAnalyzer = RetryAnalyzer.class)åç¬é
ç½®ãè¿æ ·è½æé¤ç¯å¢ä¸ç¨³å®å¯¼è´çå¶åå¤±è´¥ï¼æé«ç¨ä¾ç¨³å®æ§ã
# 18. å¦ä½çææµè¯æ¥åï¼
çæ¡è¯æ¯ï¼
æç¨Allureçææµè¯æ¥åãé¦å
å¨pom.xmléæ·»å allure-testngä¾èµï¼ç¶åå¨ä»£ç éç¨@Step注解æ è®°æµè¯æ¥éª¤ï¼ç¨@Attachmentæ·»å æªå¾éä»¶ãè·å®æµè¯åï¼æ§è¡allure generateå½ä»¤çæHTMLæ¥åãæ¥åå¾è¯¦ç»ï¼å¯ä»¥çå°æ¯ä¸ªç¨ä¾çæ§è¡æ¥éª¤ãæªå¾ãæ¥å¿ãæ§è¡æ¶é´ï¼è¿æé¥¼å¾ãè¶å¿å¾çç»è®¡ä¿¡æ¯ãæææ¥åéæå°Jenkinsï¼æµè¯å®æåèªå¨çæå¹¶åéé®ä»¶é¾æ¥ç»å¢éã
# 19. æç»éææä¹åï¼
çæ¡è¯æ¯ï¼
æä»¬ç¨Jenkinså®ç°CI/CDãå¨Jenkinsé
ç½®Maven项ç®ï¼å
³èGitä»åºï¼è®¾ç½®è§¦åå¨ï¼æ¯å¦ä»£ç æäº¤èªå¨æå»ºææ¯æå®æ¶æå»ºãæå»ºæ¥éª¤æ§è¡mvn clean test -Dbrowser=chromeï¼æå®æµè§å¨è¿è¡æµè¯ãè·å®åç¨Allureæä»¶çææ¥åï¼é
ç½®é®ä»¶éç¥åéç»ç¸å
³äººåãæä»¬è¿é
ç½®äºæµè¯å¤±è´¥è¶
è¿10%å°±ä¸å
许åå¸ï¼ç¡®ä¿ä»£ç è´¨éãæ´ä¸ªæµç¨å
¨èªå¨ï¼å¤§å¤§æåäºæµè¯æçã
# 20. æ¡æ¶è¿æåªäºå¯ä»¥ä¼åçå°æ¹ï¼
çæ¡è¯æ¯ï¼ 主è¦ä¸ä¸ªæ¹åï¼ç¬¬ä¸æ¯æ§è¡é度ï¼ç°å¨200æ¡ç¨ä¾è·30åéï¼å¯ä»¥ç¨Selenium Gridååå¸å¼å¹¶è¡æ§è¡ï¼ç¼©çå°10åéï¼ç¬¬äºæ¯ç¨³å®æ§ï¼è½ç¶æéè¯æºå¶ï¼ä½å¶å°è¿æ¯ä¼å¤±è´¥ï¼å¯ä»¥ä¼åçå¾ çç¥ï¼å¢å å¥å£®æ§ï¼ç¬¬ä¸æ¯ç»´æ¤ææ¬ï¼ç°å¨Pageç±»æç¹å¤ï¼å¯ä»¥æ½è±¡ä¸äºå ¬å ±ç»ä»¶ï¼æ¯å¦HeaderãFooterãPaginationï¼åå°éå¤ä»£ç ãè¿äºä¼åè½è®©æ¡æ¶æ´é«æå¯é ã
# ä¸ãAppèªå¨åï¼ä½ä¼ï¼
# 1. ä½ ä»¬å ¬å¸Appèªå¨åæ¯æä¹åçï¼ç¨ä»ä¹æ¡æ¶ï¼
çæ¡è¯æ¯ï¼ æä»¬ä½¿ç¨Java+Appium+TestNGæå»ºAppèªå¨åæ¡æ¶ã
æ¡æ¶åå±è®¾è®¡ï¼æä¸å±æ¯TestCaseæµè¯ç¨ä¾å±ï¼ä½¿ç¨TestNG管çç¨ä¾ï¼ç¬¬äºå±æ¯PageObject页é¢å¯¹è±¡å±ï¼å°è£ æ¯ä¸ªé¡µé¢çå ç´ åæä½æ¹æ³ï¼ç¬¬ä¸å±æ¯Driver驱å¨å±ï¼å°è£ Appiumå¯å¨ååºç¡æä½ï¼åºå±æ¯Utilså·¥å ·ç±»ï¼å å«çå¾ ãæªå¾ãæ¥å¿çå ¬å ±æ¹æ³ã
主è¦è¦ççµåAppçæ ¸å¿æµç¨ï¼ååæç´¢ãå è´ãä¸åãæ¯ä»çå ³é®ä¸å¡åºæ¯ãæ»å ±ç¼å200夿¡èªå¨åç¨ä¾ï¼éè¿Jenkinså®ç°æ¯æ¥èªå¨æ§è¡ï¼å¤§å¤§æåäºå彿µè¯æçã
# 2. 为ä»ä¹éæ©Appiumè䏿¯å ¶ä»æ¡æ¶ï¼
çæ¡è¯æ¯ï¼ éæ©Appiumä¸»è¦æä¸ä¸ªåå ï¼
ç¬¬ä¸æ¯è·¨å¹³å°ï¼ä¸å¥ä»£ç å¯ä»¥åæ¶æµè¯AndroidåiOSï¼ä¸éè¦åå«ç»´æ¤ä¸¤å¥èæ¬ï¼éä½äºç»´æ¤ææ¬ã
ç¬¬äºæ¯æ¯æå¤è¯è¨ï¼æä»¬å¢éç¨Javaå¼åï¼Appiumæ¯æJavaãPythonçå¤ç§è¯è¨ï¼ä¸éè¦é¢å¤å¦ä¹ ææ¬ã
ç¬¬ä¸æ¯å¼æºç¤¾åºæ´»è·ï¼éå°é®é¢å®¹ææ¾å°è§£å³æ¹æ¡ï¼ææ¡£åæç¨ä¹æ¯è¾ä¸°å¯ã
ç¸æ¯UIAutomatoråªæ¯æAndroidãEspressoéè¦ä¿®æ¹æºç ï¼Appiumæ´éåæä»¬çå®é éæ±ã
# 3. Appiumçå·¥ä½åçæ¯ä»ä¹ï¼
çæ¡è¯æ¯ï¼ Appiuméç¨å®¢æ·ç«¯-æå¡å¨æ¶æã
工使µç¨æ¯ï¼é¦å æµè¯èæ¬éè¿WebDriveråè®®åéHTTP请æ±ç»Appium Serverï¼Appium Serveræ¥æ¶è¯·æ±åï¼æ ¹æ®å¹³å°ç±»åï¼AndroidæiOSï¼è°ç¨ç¸åºç驱å¨ï¼Androidç¨UIAutomator2ï¼iOSç¨XCUITestï¼ç¶å驱å¨å°æä»¤ç¿»è¯æææºè½çè§£çå½ä»¤ï¼å¨çæºææ¨¡æå¨ä¸æ§è¡æä½ï¼æåå°æ§è¡ç»æè¿åç»Appium Serverï¼åè¿åç»æµè¯èæ¬ã
æ´ä¸ªè¿ç¨ä¸éè¦ä¿®æ¹Appæºç ï¼éè¿ç³»ç»èªå¸¦çæµè¯æ¡æ¶æ¥é©±å¨åºç¨ã
# 4. å¦ä½å®ä½Appå ç´ ï¼å¸¸ç¨åªäºå®ä½æ¹å¼ï¼
çæ¡è¯æ¯ï¼ æå¸¸ç¨çå®ä½æ¹å¼æäºç§ï¼
ç¬¬ä¸æ¯idå®ä½ï¼è¿æ¯æç¨³å®çæ¹å¼ï¼æ¯å¦driver.findElement(By.id("com.app:id/search"))ã
ç¬¬äºæ¯xpathå®ä½ï¼æ¯è¾çµæ´»ï¼éåå¤æåºæ¯ï¼æ¯å¦//android.widget.TextView[@text='ç«å³è´ä¹°']ã
ç¬¬ä¸æ¯classNameå®ä½ï¼éåæ¹éè·ååç±»åå ç´ ã
ç¬¬åæ¯accessibility idå®ä½ï¼éè¿content-desc屿§å®ä½ï¼å¯¹æ éç¢åè½å好ã
ç¬¬äºæ¯uiautomatorå®ä½ï¼è¿æ¯Androidç¹æçï¼åè½å¼ºå¤§ï¼æ¯å¦new UiSelector().text("å å ¥è´ç©è½¦")ã
å®é å·¥ä½ä¸ï¼æä¼å ç¨idï¼å ¶æ¬¡ç¨accessibility idï¼å¤æåºæ¯æç¨xpathï¼å 为xpathæ§è½ç¸å¯¹è¾å·®ã
# 5. å¦ä½è·åAppå ç´ çå®ä½ä¿¡æ¯ï¼
çæ¡è¯æ¯ï¼ 主è¦ç¨ä¸¤ä¸ªå·¥å ·ï¼
Android端ç¨uiautomatorviewerï¼è¿æ¯Android SDKèªå¸¦çå·¥å ·ãå æå¼Appå°ç®æ 页é¢ï¼ç¶åè¿è¡uiautomatorviewerï¼ç¹å»å·¦ä¸è§æªå¾æé®ï¼å°±è½çå°é¡µé¢å±çº§åæ¯ä¸ªå ç´ çresource-idãclassãtextç屿§ã
iOS端ç¨Appium DesktopçInspectoråè½ï¼è¿æ¥ææºåå¯ä»¥å®æ¶æ¥ç页é¢ç»æåå ç´ å±æ§ã
å¦å¤ä¹å¯ä»¥ç´æ¥å¨ä»£ç éæå°driver.getPageSource()æ¥æ¥ç页é¢ç»æï¼ä¸è¿è¿ç§æ¹å¼ä¸å¤ªç´è§ã
# 6. éå°å ç´ å®ä½ä¸å°æä¹åï¼
çæ¡è¯æ¯ï¼ æä¸è¬ä»ä¸ä¸ªæ¹é¢ææ¥ï¼
ç¬¬ä¸æ¯æ£æ¥å®ä½è¡¨è¾¾å¼æ¯å¦æ£ç¡®ï¼ç¨uiautomatorviewer确认å ç´ å±æ§æ¯å¦ååï¼ç¹å«æ¯å¨æidçæ åµã
ç¬¬äºæ¯å æ¾å¼çå¾ ï¼å 为å ç´ å¯è½è¿æ²¡å è½½åºæ¥ï¼æä¼ç¨WebDriverWaité åExpectedConditions.presenceOfElementLocatedçå¾ å ç´ åºç°ã
ç¬¬ä¸æ¯æ£æ¥æ¯å¦å¨WebViewéï¼å¦ææ¯H5页é¢éè¦å 忢contextï¼driver.context("WEBVIEW_com.app")ï¼ç¶ååå®ä½ã
è¿æå°±æ¯æ£æ¥å ç´ æ¯å¦è¢«å ¶ä»å ç´ é®æ¡ï¼æè å¨å±å¹å¯è§èå´ä¹å¤éè¦å æ»å¨ã
# 7. å¦ä½å¤çToastæç¤ºæ¡ï¼
çæ¡è¯æ¯ï¼ Toastæç¤ºæ¡æ¯è¾ç¹æ®ï¼å®ä¸æ¯æ åæ§ä»¶ï¼å®ä½æ¹å¼æä¸¤ç§ï¼
第ä¸ç§æ¯ç¨xpathå®ä½ï¼Toastçclassæ¯android.widget.Toastï¼å¯ä»¥è¿æ ·åï¼
driver.findElement(By.xpath("//*[@class='android.widget.Toast']")).getText()
第äºç§æ¯ç¨uiautomatorå®ä½ï¼è¿ä¸ªæ´ç¨³å®ï¼
driver.findElement(By.xpath("//*[contains(@text,'æ·»å æå')]"))
å 为Toastæ¾ç¤ºæ¶é´çï¼æä»¥è¦è®¾ç½®è¾ççéå¼çå¾ æ¶é´ï¼æè ç¨æ¾å¼çå¾ é åçè¶ æ¶æ¶é´ãæä¸è¬è®¾ç½®3ç§è¶ æ¶ï¼å¦æå®ä½ä¸å°å°±è®¤ä¸ºToast没åºç°ã
# 8. å¦ä½å®ç°Appçæ»å¨æä½ï¼
çæ¡è¯æ¯ï¼ æ»å¨æä½ä¸»è¦æä¸¤ç§æ¹å¼ï¼
第ä¸ç§æ¯ç¨TouchActionç±»ï¼éåç®åçä¸ä¸å·¦å³æ»å¨ï¼
TouchAction action = new TouchAction(driver);
action.press(PointOption.point(500, 1500))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point(500, 500))
.release().perform();
第äºç§æ¯ç¨Androidçuiautomatorå½ä»¤ï¼æ¯å¦æ»å¨æ¥æ¾å ç´ ï¼
driver.findElement(MobileBy.AndroidUIAutomator(
"new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text(\"åå详æ
\"))"
));
å®é 项ç®ä¸ï¼æä¸è¬å°è£ æå ¬å ±æ¹æ³ï¼ä¼ å ¥æ»å¨æ¹å忬¡æ°ï¼æ¹ä¾¿å¤ç¨ã
# 9. PageObjectæ¨¡å¼æ¯ä»ä¹ï¼ä¸ºä»ä¹è¦ç¨å®ï¼
çæ¡è¯æ¯ï¼ PageObjectæ¯é¡µé¢å¯¹è±¡æ¨¡å¼ï¼ææ¯ä¸ªé¡µé¢å°è£ æä¸ä¸ªç±»ï¼é¡µé¢å ç´ ä½ä¸ºç±»ç屿§ï¼é¡µé¢æä½ä½ä¸ºç±»çæ¹æ³ã
ä¼ç¹ä¸»è¦æä¸ä¸ªï¼
ç¬¬ä¸æ¯æé«å¯ç»´æ¤æ§ï¼å½é¡µé¢å ç´ ååæ¶ï¼åªéè¦ä¿®æ¹å¯¹åºçPageç±»ï¼ä¸ç¨æ¹ææç¨ä¾ã
ç¬¬äºæ¯æé«å¤ç¨æ§ï¼å¤ä¸ªç¨ä¾å¯ä»¥è°ç¨åä¸ä¸ªPageç±»çæ¹æ³ï¼é¿å 代ç éå¤ã
ç¬¬ä¸æ¯æé«å¯è¯»æ§ï¼ç¨ä¾ä»£ç å徿´æ¸ æ°ï¼æ¯å¦loginPage.login("ç¨æ·å", "å¯ç ")æ¯ç´æ¥åä¸å findElementè¦å®¹æçè§£ã
æä»¬é¡¹ç®ä¸æé¦é¡µãååå表页ã详æ 页ãè´ç©è½¦é¡µã订å页é½å°è£ æäºPageç±»ï¼ç¨ä¾åªéè¦è°ç¨ç¸åºçæ¹æ³å°±è¡ã
# 10. å¦ä½å¤çAppçæéå¼¹çªï¼
çæ¡è¯æ¯ï¼ æéå¼¹çªä¸»è¦æä¸¤ç§å¤çæ¹å¼ï¼
第ä¸ç§æ¯å¨ä»£ç éèªå¨ç¹å»å 许ï¼å®ä½"å 许"æ"å§ç»å 许"æé®å¹¶ç¹å»ï¼
try {
driver.findElement(By.id("com.android.packageinstaller:id/permission_allow_button")).click();
} catch (Exception e) {
// å¼¹çªä¸åå¨ï¼ç»§ç»æ§è¡
}
第äºç§æ¯ç¨Appiumçèªå¨ææåè½ï¼å¨åå§ådriveræ¶è®¾ç½®capabilitiesï¼
capabilities.setCapability("autoGrantPermissions", true);
è¿æ ·Appå®è£ åä¼èªå¨æäºæææéï¼ä¸ä¼å¼¹çªã
æä»¬é¡¹ç®ç¨çæ¯ç¬¬äºç§ï¼æ´ç®åå¯é ãå¦ææ¯æµè¯æéæç»åºæ¯ï¼æä¼æå¨ç¹å»æç»æé®ã
# 11. å¦ä½å®ç°å¤è®¾å¤å¹¶è¡æµè¯ï¼
çæ¡è¯æ¯ï¼ å¤è®¾å¤å¹¶è¡ä¸»è¦éè¿TestNGçå¹¶è¡åè½å®ç°ã
é¦å å¨testng.xmlé ç½®å¹¶è¡æ¨¡å¼ï¼
<suite name="Suite" parallel="tests" thread-count="3">
ç¶åå¨ä»£ç éæ ¹æ®ä¸å设å¤åå§åä¸åçdriverï¼ç¨ThreadLocalä¿è¯çº¿ç¨å®å ¨ï¼
private static ThreadLocal<AndroidDriver> driver = new ThreadLocal<>();
å¯å¨æ¶ä¼ å ¥ä¸å设å¤çudidå端å£å·ï¼è®©æ¯ä¸ªçº¿ç¨è¿æ¥ä¸å设å¤ã
æä»¬å ¬å¸æ3å°æµè¯æºï¼é 置好åå¯ä»¥åæ¶å¨3å°è®¾å¤ä¸è·ç¨ä¾ï¼æµè¯æ¶é´ä»1å°æ¶ç¼©çå°20åéã
# 12. å¦ä½å¤çWebViewæ··å页é¢ï¼
çæ¡è¯æ¯ï¼ WebViewæ¯H5页é¢åµå ¥å¨Appéï¼éè¦åæ¢contextæè½å®ä½ã
å¤çæ¥éª¤æ¯ï¼
ç¬¬ä¸æ¥ï¼è·åææcontextï¼
Set<String> contexts = driver.getContextHandles();
ç¬¬äºæ¥ï¼åæ¢å°WEBVIEWï¼
for (String context : contexts) {
if (context.contains("WEBVIEW")) {
driver.context(context);
break;
}
}
ç¬¬ä¸æ¥ï¼åæä½ç½é¡µä¸æ ·å®ä½å ç´ ï¼å¯ä»¥ç¨idãnameãxpathçã
ç¬¬åæ¥ï¼æä½å®æååååçï¼
driver.context("NATIVE_APP");
æä»¬çµå项ç®çåå详æ 页就æ¯H5ï¼æµè¯æ¶éè¦è¿æ ·åæ¢ã
# 13. å¦ä½å®ç°æªå¾åè½ï¼
çæ¡è¯æ¯ï¼ æªå¾ä¸»è¦ç¨äºå¤±è´¥æ¶ä¿åç°åºï¼ä¾¿äºé®é¢å®ä½ã
æå°è£ äºä¸ä¸ªæªå¾æ¹æ³ï¼
public void takeScreenshot(String name) {
File srcFile = driver.getScreenshotAs(OutputType.FILE);
String path = "./screenshots/" + name + "_" + System.currentTimeMillis() + ".png";
FileUtils.copyFile(srcFile, new File(path));
}
ç¶åå¨TestNGç失败çå¬å¨éè°ç¨ï¼
@Override
public void onTestFailure(ITestResult result) {
takeScreenshot(result.getName());
}
è¿æ ·æ¯æ¬¡ç¨ä¾å¤±è´¥é½ä¼èªå¨æªå¾ï¼æªå¾ä¼ä¿åå¨screenshotsç®å½ä¸ï¼æä»¶åå å«ç¨ä¾ååæ¶é´æ³ã
# 14. å¦ä½çææµè¯æ¥åï¼
çæ¡è¯æ¯ï¼ æä»¬ä¸»è¦ç¨ä¸¤ç§æ¹å¼çææ¥åï¼
第ä¸ç§æ¯TestNGèªå¸¦çæ¥åï¼æ§è¡å®èªå¨çætest-outputç®å½ï¼é颿index.htmlï¼å å«ç¨ä¾æ§è¡æ åµãéè¿çã失败åå çã
第äºç§æ¯Allureæ¥åï¼æ´ç¾è§å详ç»ãå¨pom.xmlæ·»å Allureä¾èµï¼ç¨ä¾ä¸å @Descriptionçæ³¨è§£ï¼æ§è¡å®åè¿è¡allure generateçææ¥åï¼æ¯æå¤±è´¥æªå¾ãæ§è¡è¶å¿ãç¨ä¾åç±»çåè½ã
æ¥åçæåï¼éè¿Jenkinsèªå¨åéé®ä»¶ç»ç¸å ³äººåï¼é®ä»¶éå 嫿µè¯æ¦åµåæ¥å龿¥ã
# 15. å¦ä½å¤çAppå¯å¨æ ¢çé®é¢ï¼
çæ¡è¯æ¯ï¼ Appå¯å¨æ ¢ä¼å¯¼è´å ç´ å®ä½è¶ æ¶ï¼æçå¤çæ¹æ³æä¸ä¸ªï¼
ç¬¬ä¸æ¯è®¾ç½®åççéå¼çå¾ æ¶é´ï¼ç»Appè¶³å¤çå¯å¨æ¶é´ï¼
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
ç¬¬äºæ¯å¨å¯å¨åå ä¸ä¸ªæ¾å¼çå¾ ï¼çå¾ é¦é¡µå ³é®å ç´ åºç°ï¼
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("é¦é¡µå
ç´ id")));
ç¬¬ä¸æ¯ç¨Appiumçcapabilityæ§å¶å¯å¨åæ°ï¼
capabilities.setCapability("appWaitActivity", "主Activity");
capabilities.setCapability("appWaitDuration", 30000);
è¿æ ·Appiumä¼çå¾ æå®Activityåºç°æè®¤ä¸ºå¯å¨å®æã
# 16. èªå¨åæ¡æ¶æ¯å¦ä½åå±çï¼
çæ¡è¯æ¯ï¼ æä»¬çæ¡æ¶éç¨åå±è®¾è®¡ï¼å为äºå±ï¼
æä¸å±æ¯TestCaseæµè¯ç¨ä¾å±ï¼ç¨TestNG管çç¨ä¾ï¼ä½¿ç¨@Test注解ï¼è°ç¨Page屿¹æ³å®æä¸å¡æµç¨æµè¯ã
第äºå±æ¯PageObject页é¢å¯¹è±¡å±ï¼æ¯ä¸ªé¡µé¢å¯¹åºä¸ä¸ªPageç±»ï¼å°è£ 页é¢å ç´ å®ä½åæä½æ¹æ³ã
第ä¸å±æ¯Driver驱å¨å±ï¼å°è£ Appiumå¯å¨ãå ³éååºç¡æä½ï¼æä¾ç»ä¸çdriverå®ä¾ã
第å屿¯Utilså·¥å ·ç±»å±ï¼å å«çå¾ ãæ»å¨ãæªå¾ãæ¥å¿ãé 置读åçå ¬å ±æ¹æ³ã
åºå±æ¯Configé ç½®å±ï¼ç¨propertiesæä»¶åå¨è®¾å¤ä¿¡æ¯ãAppè·¯å¾ãæå¡å¨å°åçé ç½®ï¼æ¹ä¾¿åæ¢ç¯å¢ã
è¿ç§åå±ç»æèè´£æ¸ æ°ï¼ä¿®æ¹æä¸å±ä¸å½±åå ¶ä»å±ï¼ä¾¿äºç»´æ¤åæ©å±ã
# 17. å¦ä½å®ç°æ°æ®é©±å¨æµè¯ï¼
çæ¡è¯æ¯ï¼ æ°æ®é©±å¨ä¸»è¦ç¨TestNGç@DataProviderå®ç°ã
æä¼ææµè¯æ°æ®æ¾å¨Excelæpropertiesæä»¶éï¼ç¶åç¨@DataProvider读åï¼
@DataProvider(name = "loginData")
public Object[][] getLoginData() {
return new Object[][] {
{"user1", "pass1"},
{"user2", "pass2"},
{"user3", "pass3"}
};
}
@Test(dataProvider = "loginData")
public void testLogin(String username, String password) {
loginPage.login(username, password);
}
è¿æ ·ä¸ä¸ªæµè¯æ¹æ³å°±è½è·å¤ç»æ°æ®ï¼ç¨äºæµè¯ä¸åè´¦å·ç»å½ãä¸åååä¸åçåºæ¯ï¼é¿å åéå¤ä»£ç ã
æä»¬é¡¹ç®ç¨Excelç®¡çæµè¯æ°æ®ï¼ç¨Apache POI读åï¼è¿æ ·æµè¯äººåå¯ä»¥ç´æ¥ä¿®æ¹Excelï¼ä¸ç¨æ¹ä»£ç ã
# 18. éå°è¿ä»ä¹é¾ç¹é®é¢ï¼å¦ä½è§£å³çï¼
çæ¡è¯æ¯ï¼ æéå°è¿ä¸ä¸ªæ¯è¾æ£æçé®é¢ï¼ååå表æ»å¨å è½½æ´å¤æ¶ï¼å ç´ å®ä½ä¸ç¨³å®ï¼ç»å¸¸æ¥NoSuchElementExceptionã
åæååç°æ¯å 为å表éç¨æå è½½ï¼æ»å¨æ¶æ§å ç´ ä¼è¢«åæ¶ï¼æ°å ç´ å¨æå è½½ï¼å¯¼è´å®ä½æ¶å ç´ å¯è½ä¸å¨DOMæ éã
æçè§£å³æ¹æ¡æ¯ï¼
第ä¸ï¼æ»å¨åå çå¾ å è½½å®æï¼éè¿æ£æ¥å è½½å¨ç»æ¶å¤±æ¥å¤æã
第äºï¼ç¨try-catchå 裹æ»å¨æä½ï¼å¦æå®ä½å¤±è´¥å°±éè¯ï¼æå¤éè¯3次ã
第ä¸ï¼ä¼åæ»å¨çç¥ï¼æ¯æ¬¡åªæ»å¨åå±è䏿¯æ´å±ï¼åå°å ç´ åæ¶çæ¦çã
ä¼ååï¼ç¨ä¾ç¨³å®æ§ä»70%æåå°95%以ä¸ã
# 19. å¦ä½åæç»éæï¼
çæ¡è¯æ¯ï¼ æä»¬éè¿Jenkinså®ç°æç»éæã
é ç½®æ¥éª¤æ¯ï¼
第ä¸ï¼å¨Jenkinså建èªç±é£æ ¼ä»»å¡ï¼é ç½®Gitä»åºå°åï¼å®æ¶æåææ°ä»£ç ã
第äºï¼å¨æå»ºç¯å¢é ç½®Mavenï¼æ§è¡mvn clean testå½ä»¤è¿è¡æµè¯ã
第ä¸ï¼é ç½®å®æ¶è§¦åå¨ï¼ä½¿ç¨cron表达å¼è®¾ç½®æ¯å¤©åæ¨2ç¹èªå¨æ§è¡ã
第åï¼æå»ºåæä½é ç½®Allureæ¥åçæï¼å¹¶è®¾ç½®é®ä»¶éç¥ï¼æµè¯å®æåèªå¨å鿥åç»ç¸å ³äººåã
第äºï¼å¦ææµè¯å¤±è´¥ï¼Jenkins伿 è®°æå»ºå¤±è´¥ï¼å¹¶å¨é®ä»¶éé带失败æªå¾åæ¥å¿ã
éè¿Jenkinsï¼æä»¬å®ç°äºæ¯æ¥èªå¨å彿µè¯ï¼åæ¶åç°æ°çæ¬å¼å ¥çé®é¢ã
# 20. Appèªå¨åç¸æ¯æå·¥æµè¯æä»ä¹ä¼å¿åå±éæ§ï¼
çæ¡è¯æ¯ï¼ ä¼å¿ä¸»è¦æä¸ä¸ªï¼
ç¬¬ä¸æ¯æçé«ï¼æå·¥å彿 ¸å¿æµç¨éè¦2-3人æ¥ï¼èªå¨ååªéåå°æ¶ï¼èçäºå¤§éæ¶é´ã
ç¬¬äºæ¯ç¨³å®å¯é ï¼èªå¨åæåºå®æ¥éª¤æ§è¡ï¼ä¸ä¼å 为人为çå¿½éæ¼æµè¯ç¹ã
ç¬¬ä¸æ¯æ¯ææç»éæï¼å¯ä»¥æ¯å¤©èªå¨æ§è¡ï¼åæ¶åç°é®é¢ã
ä½ä¹æå±éæ§ï¼
ç¬¬ä¸æ¯æ æ³æ¿ä»£æ¢ç´¢æ§æµè¯ï¼èªå¨ååªè½éªè¯é¢æç»æï¼åç°ä¸äºé¢æä¹å¤çé®é¢ã
ç¬¬äºæ¯ç»´æ¤ææ¬é«ï¼é¡µé¢å ç´ ååéè¦åæ¶æ´æ°èæ¬ï¼å¦åä¼å¤§é失败ã
ç¬¬ä¸æ¯ä¸éåé¢ç¹ååçåè½ï¼å¦æåè½è¿ä¸ç¨³å®ï¼èªå¨åä¼æå ¥äº§åºæ¯å¾ä½ã
æä»¥æä»¬çç¥æ¯ï¼ç¨³å®çæ ¸å¿æµç¨åèªå¨åï¼æ°åè½åæ¢ç´¢æ§æµè¯è¿æ¯æå·¥ä¸ºä¸»ã
ðæµè¯å¼åè®ç»è¥ (opens new window)ï¼é对é¢è¯æ±èçæµè¯å¼åè®ç»è¥ï¼å¤§åå¯¼å¸ 1v1 ç§æï¼å¸®å©å¦åä» 0 å° 1 æ¿å°æµå¼æ»¡æç offer
- â ç´ææè¯¾+宿¶äºå¨ççï¼è¯¾ç¨å°å°±ä¸è´¯ç©¿ä¼ä¸çº§æ¡ä¾ï¼ç±æµè¯å¼å导å¸å ¨ç¨ä¸»è®²ï¼ç»æ å ¶ä»å©çèå¸ä»£è¯¾ã
- â 大念è¯å¼å导å¸ä¸å¯¹ä¸æ±èéªè·ï¼è¦ç课ç¨çç+ç®åæç£¨+é¢è¯æ¨¡æé¢è¯+é¢è¯å¤ççæ±èè¾ å¯¼
ææ°çå¾è§£æç« é½å¨å ¬ä¼å·é¦åï¼å«å¿è®°å ³æ³¨å¦ï¼ï¼å¦æä½ æ³å å ¥ç¾äººææ¯äº¤æµç¾¤ï¼æ«ç 䏿¹äºç»´ç åå¤ãå 群ãã
