# 軟體測試期中考_1 ## 連結 https://hackmd.io/@ruserxd/rJ9Nqzj-kg ## 介紹 Introduce 設計一套可以計算當前票價的程式,並加以測試 ## 資料設計 DataDesign 第二欄的設計理念,因為假日、套票、季後賽的價格互不影響,因此把它設計為同一個欄位 Holiday -> 假日, Post -> 季後賽, Package -> 套票 | Price | no/isHoliday/isPost/isPackage | isVip | |:--------:|:-----------------------------:|:----------:| | 票的價格 | 票的模式 | 是否為 Vip | **這邊要特別對出現 (套票 + Vip) 進行例外處理** ## 程式設計 Programing ### 變數的理念 因為票價是固定的,因此將其資料型態統一設計為 `final private static double` 這樣日後票價若出現漲價、降價,也比較好一併處理 ```java // 基本的票價 private final static double BASIC_PRICE = 500; // 假日的票價 private final static double HOLIDAY_PRICE = 600; // 季後賽的票價 private final static double POST_PRICE = 1000; // 套票的票價 private final static double PACKAGE_PRICE = 250; // VIP的票價 private final static double VIP_BONUS = 0.8; ``` ### 程式邏輯 + Preventing Program 對 counter 傳入 mode, isVip 的值 首先判斷是否 mode 介於設計的 0 ~ 3 之間,若沒有則拋出例外 ```java if (mode < 0 || mode > 3) throw new IllegalAccessException("mode 應該介於 0~3之間"); ``` 透過 mode 得到一個價格 接著去判斷是否出現 VIP + (mode == 3) 的情況 ```java boolean bothVipAndModeThree = isVIP && mode == 3; if (bothVipAndModeThree) throw new IllegalAccessException("不能為套票又使用 VIP"); ``` 判斷是否為 VIP,若是則打折 先透過 log 輸出本次輸入、輸出 ```java log.info("mode {} isVIp {} 最後算出 {}", mode, isVIP, result); ``` 最後回傳結果 ### PMD 的處理 將所有遇上的問題通通解決掉 並且有將原始的程式產生的 PMD 以及修改後的 PMD 各別放上資料夾的第一層 名稱為 originPmd.html, afterPmd.html 放上一些我這之中學習到的知識 - MethodNamingConventions The static method name 'Counter' function 的命名方式應該為駱駝式的命名 - UseUtilityClass All methods are static. Consider using a utility class instead. 這邊因為 function 都是 static 的,因此建議將其設置為一個不可被繼承的 class 避免在日後我們的程式碼被人呼叫成以下 ```java CounterMLB counter = new CounterMLB(); ``` 期望的是直接呼叫 ```java CounterMLB.counter() ``` - AvoidLiteralsInIfCondition 因為我的 mode 設計為 0~3 所以我當時是寫了 ```java if (mode == 0) { } else if (mode == 1){ }..... ``` 這會造成日後,閱讀程式碼的人還要先理解 0 ~ 3 的含意是甚麼 因此我把它設計為 switch 並且額外設立一個函數 同時避免掉一個 function 有太多判斷的問題 ```java public static double judgePrice(int mode) { return switch (mode) { case 1 -> HOLIDAY_PRICE; case 2 -> POST_PRICE; case 3 -> PACKAGE_PRICE; default -> BASIC_PRICE; }; } ``` ## 測試 Testing 首先透過本課學習到的強涵蓋的知識,設計一個 csv 檔案 | Price | no/isHoliday/isPost/isPackage | isVip | |:-----:|:-----------------------------:|:-----:| | 500 | 0 | FALSE | | 600 | 1 | FALSE | | 1000 | 2 | FALSE | | 250 | 3 | FALSE | | 400 | 0 | TRUE | | 480 | 1 | TRUE | | 800 | 2 | TRUE | 1. 強涵蓋,那這邊若用數學去計算應該為 4*2 ```java @ParameterizedTest(name = "票價測試 #{index} - 預期票價:{0}, 票種:{1}, VIP:{2}") @DisplayName("測試 .csv 內設計的所有正常可能資料") @CsvFileSource(resources = "/Data.csv", numLinesToSkip = 1) void counter(double expected, int mode, boolean isVip) throws IllegalAccessException { double result = CounterMLB.counter(mode, isVip); log.info("預期: {} 測試結果: {}",expected , result); assertEquals(expected, result); } ``` 2. 為何 -1 ? 因為必須再額外寫一個測試檔案去針對出現 VIP + package 的情況 ```java @ParameterizedTest @DisplayName("測試 套票 + VIP 的組合出現") @ValueSource(ints = {3}) void counterForCheckVIPAndPackage(int mode) { IllegalAccessException illegalAccessException = assertThrows(IllegalAccessException.class, () -> CounterMLB.counter(mode, TRUE)); log.info("測試拋出接受到的 message {}", illegalAccessException.getMessage()); assertEquals("不能為套票又使用 VIP", illegalAccessException.getMessage()); } ``` 3. 最後再針對是否我們有對 mode 的邊界進行拋出例外的測試 ````java @ParameterizedTest @DisplayName("測試 mode 非規範內的拋出") @ValueSource(ints = {-1,4,5}) void checkModeInZeroToThree(int mode) { IllegalAccessException illegalAccessException = assertThrows(IllegalAccessException.class, () -> CounterMLB.counter(mode, TRUE)); log.info("測試拋出接受到的 message {}", illegalAccessException.getMessage()); assertEquals("mode 應該介於 0~3之間", illegalAccessException.getMessage()); } ```` 測試結果 ![upload_af3b290214f4cec89a63949ba30c74cd](https://hackmd.io/_uploads/r1v4hdo-1l.png) ## 自評 (O) 能夠設計豐富而有效的測試案例(以表格規劃之) (O) 完成程式可以執行,並以 JUnit 測試 (O) 成功完成 PMD 程式靜態檢測的報告 (O) 可以依據 PMD 建議,改善程式碼