題目要求
測試以下程序:該程序有三個輸入變量month、day、year(month、day和year均為整數值,並且滿足:1≤month≤12、1≤day≤31和1900≤year≤2050),分別作為輸入日期的月份、日、年份,通過程序可以輸出該輸入日期在日歷上隔一天的日期。例如,輸入為2004年11月30日,則該程序的輸出為2004年12月1日。
- 划分等價類,按照等價類划分法設計測試用例;
- 編寫getNextDate函數;
- 掌握Junit4的用法,使用Junit4測試getNextDate函數。
等價類表
假設輸入格式為year,month,day,且三個輸入變量year、month和day均被輸入。
year要區分閏年和平年,其中閏年還可以分為世紀閏年和普通閏年,且year要屬於[1900,2050]。
month要根據該月有幾天來進行區分,並且需要考慮是否向year進位,且month要屬於[1,12]。
day要根據月份來判斷天數是否合法,並且需要考慮是否向month進位,且day要屬於[1,31]。
等價類划分如下。
測試用例
有效等價類測試用例
共有5個有效等價類測試用例。
測試數據 | 期望結果 | 覆蓋范圍 |
---|---|---|
2004/12/25 | 2004/12/26 | 2,10,14 |
2001/2/28 | 2001/3/1 | 3,7,15 |
2000/2/29 | 2000/3/1 | 1,7,16 |
2001/4/30 | 2001/5/1 | 3,8,17 |
2001/5/31 | 2001/6/1 | 3,9,18 |
無效等價類測試用例
共有12個有效等價類測試用例。
測試數據 | 期望結果 | 覆蓋范圍 |
---|---|---|
1899/6/1 | year非法 | 4 |
2051/6/1 | year非法 | 5 |
a/6/1 | year非法 | 6 |
1999/0/1 | month非法 | 11 |
1999/13/1 | month非法 | 12 |
1999/a/1 | month非法 | 13 |
1999/1/0 | day非法 | 19 |
1999/1/32 | day非法 | 20 |
1999/1/a | day非法 | 21 |
2001/2/29 | day非法 | 22 |
2000/2/30 | day非法 | 23 |
2001/4/31 | day非法 | 24 |
源代碼
項目結構如下圖所示
DateUtil.java
package com.company;
public class DateUtil {
// 有31天的月份
private static int[] monthOfThirtyOne = new int[]{1,3,5,7,8,10,12};
// 有30天的月份
private static int[] monthOfThirty = new int[]{4,6,9,11};
// 年月日
private int year;
private int month;
private int day;
// 最終實現的功能,輸入是一個“年/月/日”格式的字符串;
// 如果函數運行成功,輸出則是相同格式的下一天,否則是錯誤信息
public String getNextDate(String dateStr){
String updateResult = this.updateDate(dateStr);
// 如果輸入合法
if (updateResult.equals("success")){
String checkResult = this.checkDate();
// 如果輸入合法
if (checkResult.equals("valid")){
// 計算明天的日期
return this.calcNextDate();
}
return checkResult;
}
return updateResult;
}
// 根據輸入字符串轉換並更新年月日
private String updateDate(String dateStr){
// 獲取年月日
String[] numbers = dateStr.split("/");
try{
this.year = Integer.parseInt(numbers[0]);
}catch (NumberFormatException e){
return "year非法";
}
try{
this.month = Integer.parseInt(numbers[1]);
}catch (NumberFormatException e){
return "month非法";
}
try{
this.day = Integer.parseInt(numbers[2]);
}catch (NumberFormatException e){
return "day非法";
}
return "success";
}
// 檢查日期是否合法
private String checkDate(){
String valid = "valid";
String yearInvalid = "year非法";
String monthInvalid = "month非法";
String dayInvalid = "day非法";
// year合法
if (year>=1900&&year<=2050){
// month合法
if (month>=1&&month<=12){
// day小於1
if (day<=0){
return dayInvalid;
}
// 至此能保證day大於0
// 是2月
if (month==2){
// 閏年
if (yearIsLeap(year)){
// 1-29
if (day<=29){
return valid;
}else{
return dayInvalid;
}
}
// 平年2月
else{
// 1-28
if (day<=28){
return valid;
}else{
return dayInvalid;
}
}
}
// 至此能保證不是2月
// 是否為31天的月
for(int i=0;i<7;++i){
if (month==monthOfThirtyOne[i]){
// 1-31
if (day<=31){
return valid;
}else{
return dayInvalid;
}
}
}
// 至此能保證不是2月和31天的月
// 是否為30天的月
for(int i=0;i<4;++i){
if (month==monthOfThirty[i]){
// 1-30
if (day<=30){
return valid;
}else{
return dayInvalid;
}
}
}
}
// month非法
else{
return monthInvalid;
}
}
// year非法
return yearInvalid;
}
// 計算下一天
private String calcNextDate(){
int yearNext;
int monthNext;
int dayNext=day+1;
int dayCarry=0;
int monthCarry=0;
// 處理day
// 是2月
if (month==2){
// 閏年
if (yearIsLeap(year)){
// 1-29
if (day==29){
dayNext = 1;
dayCarry = 1;
}
}
// 平年2月
else{
// 1-28
if (day==28){
dayNext = 1;
dayCarry = 1;
}
}
}
// 不是2月
else{
boolean isThirtyOne= false;
// 是否為31天的月
for(int i=0;i<7;++i){
if (month==monthOfThirtyOne[i]){
isThirtyOne = true;
// 1-31
if (day==31){
dayNext = 1;
dayCarry = 1;
}
break;
}
}
// 至此能保證是30天的月
if (!isThirtyOne){
// 1-30
if (day==30){
dayNext = 1;
dayCarry = 1;
}
}
}
// 處理月
if (month+dayCarry>12){
monthNext = 1;
monthCarry = 1;
}else{
monthNext = month+dayCarry;
}
// 處理年
yearNext = year+monthCarry;
return yearNext +"/"+ monthNext +"/"+ dayNext;
}
// 判斷某一年是否為閏年
private boolean yearIsLeap(int year){
// 普通閏年和世紀閏年
if ((year%4==0&&year%100!=0)||(year%400==0)){
return true;
}
// 平年
return false;
}
}
DateUtilTest.java
package com.test;
import com.company.DateUtil;
import static org.junit.Assert.*;
import org.junit.Test;
//1、參數化測試:引入相關的包和類
import java.util.Collection;
import java.util.Arrays;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class) //2、參數化測試:更改測試運行器為RunWith(Parameterized.class)
public class DateUtilTest {
//3、參數化測試:聲明變量用來存放預期值與結果值
private DateUtil util = new DateUtil();
private String date;
private String except;
//4、參數化測試:聲明一個返回值為 Collection 的公共靜態方法,並使用@Parameters 進行修飾
@Parameters
public static Collection data(){
return Arrays.asList(new Object[][]{
{"2004/12/25", "2004/12/26"},
{"2001/2/28", "2001/3/1"},
{"2000/2/29", "2000/3/1"},
{"2001/4/30", "2001/5/1"},
{"2001/5/31", "2001/6/1"},
{"1899/6/1", "year非法"},
{"2051/6/1", "year非法"},
{"a/6/1", "year非法"},
{"1999/0/1", "month非法"},
{"1999/13/1", "month非法"},
{"1999/a/1", "month非法"},
{"1999/1/0", "day非法"},
{"1999/1/32", "day非法"},
{"1999/1/a", "day非法"},
{"2001/2/29", "day非法"},
{"2000/2/30", "day非法"},
{"2001/4/31", "day非法"},
});
}
//5、參數化測試:為測試類聲明一個帶有參數的公共構造方法,並在其中為聲明變量賦值
public DateUtilTest(String date, String except){
this.date = date;
this.except = except;
}
@Test
public void testGetNextDate(){
assertEquals(except, util.getNextDate(date));
}
}
測試結果
如下圖所示,17個測試用例均測試成功,程序實際輸出與期望值相同。
實驗總結
本次實驗的主要目的是鞏固黑盒測試方法中的等價類划分法的知識,練習JUnit的參數化測試。在本次實驗中,我認為我的getNextDate函數的實現並不是很優雅,比較過程化。寫這個函數花了我很多時間,主要問題在於我沒有抓住一些關鍵的、抽象的邏輯和子函數,比如天向月份進位和月份向年份完全可以參照加法器的循環、可以寫一個函數根據年份和月份判斷出天數的最大值等等。
作者:@臭咸魚
轉載請注明出處:https://www.cnblogs.com/chouxianyu/
歡迎討論和交流!