计算某天的下一天:黑盒测试之等价类划分+JUnit参数化测试

题目要求

测试以下程序:该程序有三个输入变量month、day、year(month、day和year均为整数值,并且满足:1≤month≤12、1≤day≤31和1900≤year≤2050),分别作为输入日期的月份、日、年份,通过程序可以输出该输入日期在日历上隔一天的日期。例如,输入为2004年11月30日,则该程序的输出为2004年12月1日。

  1. 划分等价类,按照等价类划分法设计测试用例;
  2. 编写getNextDate函数;
  3. 掌握Junit4的用法,使用Junit4测试getNextDate函数。

等价类表

假设输入格式为year,month,day,且三个输入变量year、month和day均被输入。

year要区分闰年和平年,其中闰年还可以分为世纪闰年和普通闰年,且year要属于[1900,2050]。

month要根据该月有几天来进行区分,并且需要考虑是否向year进位,且month要属于[1,12]。

day要根据月份来判断天数是否合法,并且需要考虑是否向month进位,且day要属于[1,31]。

等价类划分如下。

等价类划分表.jpg

测试用例

有效等价类测试用例

共有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

源代码

项目结构如下图所示

项目结构.jpg

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个测试用例均测试成功,程序实际输出与期望值相同。

测试结果.jpg

实验总结

本次实验的主要目的是巩固黑盒测试方法中的等价类划分法的知识,练习JUnit的参数化测试。在本次实验中,我认为我的getNextDate函数的实现并不是很优雅,比较过程化。写这个函数花了我很多时间,主要问题在于我没有抓住一些关键的、抽象的逻辑和子函数,比如天向月份进位和月份向年份完全可以参照加法器的循环、可以写一个函数根据年份和月份判断出天数的最大值等等。


作者:@臭咸鱼

转载请注明出处:https://www.cnblogs.com/chouxianyu/

欢迎讨论和交流!


posted @ 2020-04-28 03:14  臭咸鱼  阅读(4903)  评论(0编辑  收藏  举报