以下內(nèi)容為個人理解,有不足之處還望指正......
背景:我們多數(shù)時候,測試都是基于表層現(xiàn)象看問題,部分缺陷很容易發(fā)現(xiàn)(開發(fā)代碼寫錯),但因?yàn)闇y試場景的遺漏,導(dǎo)致上線出問題。所以我們需要對開發(fā)的代碼有整體了解。
目的:1.加強(qiáng)對需求實(shí)現(xiàn)的了解;2.對項(xiàng)目整體可以做到心中有數(shù),以確定測試邊界(有疑問或不理解的地方,可以和開發(fā)進(jìn)行思維碰撞;了解越多,測試設(shè)計可以更加充分);3.識別一些低級別缺陷。
以下一、二、三大點(diǎn)是走讀代碼的前置條件
一.了解Java項(xiàng)目關(guān)鍵字 (部分參考網(wǎng)上)
在一般工程中,基本上都會出現(xiàn)controller、service、impl、Dao等關(guān)鍵字,來說下這些關(guān)鍵字之間的聯(lián)系
Controller:負(fù)責(zé)具體的業(yè)務(wù)模塊流程控制,在此層里面要調(diào)用Serice層的接口來控制業(yè)務(wù)流程,控制的配置是在Spring的配置文件里面進(jìn)行,針對具體的業(yè)務(wù)流程,會有不同的控制器。
Service:Service層主要負(fù)責(zé)業(yè)務(wù)模塊的邏輯應(yīng)用設(shè)計。同樣是首先設(shè)計接口,再設(shè)計其實(shí)現(xiàn)的類(impl),接著再Spring的配置文件中配置其實(shí)現(xiàn)的關(guān)聯(lián)。Service層的業(yè)務(wù)實(shí)現(xiàn),具體要調(diào)用到已定義的Dao層的接口。
Dao:Dao層主要是做數(shù)據(jù)持久層的工作,負(fù)責(zé)與數(shù)據(jù)庫進(jìn)行聯(lián)絡(luò)的一些任務(wù)都封裝在此,Dao層的設(shè)計首先是設(shè)計Dao的接口,然后在Spring的配置文件中定義此接口的實(shí)現(xiàn)類,然后就可在模塊中調(diào)用此接口來進(jìn)行數(shù)據(jù)業(yè)務(wù)的處理,而不用關(guān)心此接口的具體實(shí)現(xiàn)類是哪個類,顯得結(jié)構(gòu)非常清晰,Dao層的數(shù)據(jù)源配置,以及有關(guān)數(shù)據(jù)庫連接的參數(shù)都在Spring的配置文件中進(jìn)行配置。
二.了解maven
1.簡介參考maven(一) maven到底是個啥玩意~。
2.安裝maven查看。
3.公司私庫配置。
配置完成后,在IDEA>Terminal中跑sh package.sh ,出現(xiàn)如下所示 就代表成功導(dǎo)入 公司私庫內(nèi)的jar包。

4.做以上操作的目的?
為了能在Java項(xiàng)目 代碼走讀時看到Dao內(nèi)方法的具體實(shí)現(xiàn)(對數(shù)據(jù)庫的增刪改查)。
三.了解pom.xml文件
pom是項(xiàng)目對象模型(Project Object Model)的簡稱,它是Maven項(xiàng)目中的文件,使用XML表示,名稱叫做pom.xml。作用類似ant的build.xml文件,功能更強(qiáng)大。該文件用于管理:源代碼、配置文件、開發(fā)者的信息和角色、問題追蹤系統(tǒng)、組織信息、項(xiàng)目授權(quán)、項(xiàng)目的url、項(xiàng)目的依賴關(guān)系等等。詳細(xì)可查看
四.小白實(shí)操 代碼走讀
示例 project:dada-api-learning branch:feature/LEAR-1781

?前戲?
1.到公司gitlab獲取工程,克隆倉庫到本地。
git clone git@xxxxxxxxxxxxxx/dada-api-learning.git;然后 git checkout feature/LEAR-1781 ----git命令不熟的自己去補(bǔ)
2.打開IntelliJ IDEA讀取工程。
?開始?
思考下,整個工程那么多代碼,我們應(yīng)重點(diǎn)關(guān)注什么???
我的理解:后端代碼主要實(shí)現(xiàn)業(yè)務(wù)數(shù)據(jù)的增刪改查,將數(shù)據(jù)返回客戶端。
故而,我們關(guān)注的重點(diǎn)就是由客戶端發(fā)送HTTP請求--->后端接收請求--->進(jìn)行邏輯處理,提取數(shù)據(jù)--->將數(shù)據(jù)返回客戶端。這整個過程。
那么,咱們詳細(xì)解讀下整個過程 (示例API:/v2/learning/yearendreport/getReport)
*首先,客戶端發(fā)來的HTTP請求是由Controller接收的
那么我們要找到Controller目錄(在src/main/java/com.dadaabc.api.learning/controller),然后再找對應(yīng)業(yè)務(wù)命名的Controller文件進(jìn)入 || 若不知文件名,可以復(fù)制對應(yīng)API 包含的方法名,在IDEA中雙擊shift打開搜索框搜索進(jìn)入

Controller層詳解:
1.@RestController關(guān)鍵字會接收客戶端發(fā)來的HTTP請求,然后用@RequestMapping關(guān)鍵字去匹配對應(yīng)路徑的請求(關(guān)鍵字后跟的("/v2/learning/yearendreport")就是對應(yīng)接口的前置路徑)
2.如果匹配成功,然后就執(zhí)行下面的YearEndReportController類
1)private static final Logger logger = LoggerFactory.getLogger(YearEndReportController.class); 意思是使用指定YearEndReportController類初始化日志對象,方便在日志輸出的時候,打印日志信息。
2)自動裝配了這個接口的業(yè)務(wù)實(shí)現(xiàn)邏輯(service),具體為什么會在這個地方引入,咱們先不深究。
@Autowired
private YearEndReportService yearEndReportService;
@Autowired
private YearEndReportInitService initService;
@Autowired
private AsyncService asyncService;
3.@RequestMapping(value = "/getReport", method = RequestMethod.POST) 這塊就是真正匹配接口了,意思是以/v2/learning/yearendreport/getReport結(jié)尾的post請求會匹配執(zhí)行g(shù)etReport()方法
1) @RequestBody JSONObject jsonObject 意思是接收的參數(shù)來自于requestBody中(即請求體),且傳參為json格式。
2)Integer studentId = jsonObject.getInteger("studentId");表示去取前端傳遞的參數(shù)(參數(shù)為整型)
4.以下代碼表示先校驗(yàn)學(xué)生id,若id不存在就直接返回錯誤code碼和message 至客戶端。
if (studentId == null) {
logger.info("getReport studentId is null");
return ErrorCode.BAD_PARAMETER;
}
5.以下代碼中的yearEndReportService就是具體業(yè)務(wù)邏輯實(shí)現(xiàn)的類(service層),getReportByStudentId就是業(yè)務(wù)實(shí)現(xiàn)的具體方法;可以按住Ctrl鍵,點(diǎn)擊getReportByStudentId跳轉(zhuǎn)查看具體實(shí)現(xiàn)。(這一步就跳轉(zhuǎn)到了業(yè)務(wù)具體實(shí)現(xiàn)的service層)
ResponseData responseData = yearEndReportService.getReportByStudentId(studentId);
下圖為Service層代碼(這塊是設(shè)計接口,具體實(shí)現(xiàn)邏輯在ResponseData getReportByStudentId(int studentId);)
點(diǎn)左側(cè) 箭頭指向處 可跳轉(zhuǎn)查看具體實(shí)現(xiàn)的類(impl)
下圖為Service具體實(shí)現(xiàn)的類(impl)方法,這塊也就是 核心業(yè)務(wù)邏輯

Service層詳解:
1.YearEndReport report = yearEndReportDao.findByStudentId(studentId); 這塊意思是在yearEndReportDao下的findByStudentId方法里去查學(xué)生的信息,需傳入studentId,將查出的結(jié)果賦值給report變量。 ---Dao層在最后提及
2.以下代碼的意思是,如果查表得出的學(xué)生信息不存在,就去調(diào)createNewUserReport方法實(shí)時創(chuàng)建用戶的報告(需傳入學(xué)生id);若實(shí)時創(chuàng)建時也找不到學(xué)生信息,那么就打印日志說“這個學(xué)生依舊沒有報告”,并且return出去(將code碼和"null"返回客戶端);若創(chuàng)建報告成功,那么就將報告數(shù)據(jù)插入到y(tǒng)earEndReport表中
if (report == null) {
report = createNewUserReport(studentId);
if (report == null) {
logger.info("getReportByStudentId this student still dont have studentId:{}", studentId);
return new ResponseData(ResponseData.CODE_SUCCESS, report);
}
logger.info("getReportByStudentId this student is new,studentId:{},newReport:{}", studentId, report);
yearEndReportDao.insertReport(report);
}
3.JSONObject jsonReport = JSONObject.parseObject(reportInfo); 意思是將報告內(nèi)容轉(zhuǎn)換成json格式
4.以下代碼的意思是,更新 studentName/studentAvatar/likeReport/avatarInfo這4項(xiàng)信息到報告內(nèi)容中去,最后將所有信息返回客戶端(包括code_success狀態(tài)碼和json格式的報告內(nèi)容)
JSONObject jsonReport = JSONObject.parseObject(reportInfo);
jsonReport.put("studentName", report.getStudentName());
jsonReport.put("studentAvatar", report.getStudentAvatar());
jsonReport.put("likeReport", report.getLikeReport());
String avatarInfo = jsonReport.getString("avatarInfo");
jsonReport.put("avatarInfo", JSONObject.parseObject(avatarInfo));
return new ResponseData(ResponseData.CODE_SUCCESS, jsonReport);
下圖為Dao層代碼:
Dao入口:(點(diǎn)擊截圖箭頭指向區(qū)域跳轉(zhuǎn)查看)
下圖是實(shí)現(xiàn)Dao的接口,具體邏輯都在YearEndReport findByStudentId(int studentId);內(nèi),點(diǎn)擊圖中箭頭指向處,跳轉(zhuǎn)查看SQL語句

Dao層詳解:
下圖就是上面跳轉(zhuǎn)過來的具體的實(shí)現(xiàn)(SQL查詢),其中#{studentId} 讀取的是跳轉(zhuǎn)前傳過來的參數(shù)
SQL : SELECT id,student_id,student_name, student_avatar,like_report, report_info FROM year_end_report WHERE student_id = #{studentId} order by id desc limit 1;
讀到這里,后端接收HTTP請求—>處理數(shù)據(jù)—>將數(shù)據(jù)返回客戶端,這整個流程的代碼算是走讀完畢。
通過以上步驟,你應(yīng)該知道這個接口到底查了什么東西,與你的預(yù)期結(jié)果是否一致。