• 20155328 《Java程序设计》 实验二(Java面向对象程序设计) 实验报告


    20155328 《Java程序设计》 实验二(Java面向对象程序设计) 实验报告

    单元测试

    一、单元测试和TDD

    • 编程时需理清思路,将编程需求等想好,再开始编。此部分可用伪代码实现。

    • 用编程语言将伪代码翻译一下,就是产品代码了。伪代码是产品代码最好的注释。

    • 写了产品代码,还要写测试代码来证明自己的代码没有问题。Java编程时对类实现的测试叫单元测试。

    关于单元测试的练习

    需求:在一个MyUtil类中解决一个百分制成绩转成“优、良、中、及格、不及格”五级制成绩的功能。

    根据需求撰写伪代码

    百分制转五分制:
       如果成绩小于60,转成“不及格”
       如果成绩在60与70之间,转成“及格”
       如果成绩在70与80之间,转成“中等”
       如果成绩在80与90之间,转成“良好”
       如果成绩在90与100之间,转成“优秀”
       其他,转成“错误”
    

    用Java程序语言翻译伪代码为产品代码

    package experiment2;
    public class MyUtil {
        public static String percentage2fivegrade(int grade){
            //如果成绩小于0,转成“错误”
            if (grade<0) return "错误";
            //如果成绩小于60,转成“不及格”
            if (grade<60) return "不及格";
            //如果成绩在60到70之间,转成“及格”
            else if (grade<70) return "及格";
            //如果成绩在70到80之间,转成“中等”
            else if (grade<80) return "中等";
            //如果成绩在80到90之间,转成“良好”
            else if (grade<90) return "良好";
            //如果成绩在90到100之间,转成“优秀”
            else if (grade<=100) return "优秀";
            //其他,转成“错误”
            else return "错误";
        }
    }
    

    接下来就是撰写测试代码,判断自己的产品代码有没有出错了: 选中MyUtil,在左边出现的小灯泡下拉菜单中选择Create Test:

    接着在创建出的MyUtilTest撰写测试代码:

    import junit.framework.TestCase;
    import org.junit.Test;
    public class MyUtilTest extends TestCase {
        @Test
        public void testNormal() {
            assertEquals("不及格", MyUtil.percentage2fivegrade(55));
            assertEquals("及格", MyUtil.percentage2fivegrade(65));
            assertEquals("中等", MyUtil.percentage2fivegrade(75));
            assertEquals("良好", MyUtil.percentage2fivegrade(85));
            assertEquals("优秀", MyUtil.percentage2fivegrade(95));
        }
    
        @Test
        public void testException(){
            assertEquals("错误",MyUtil.percentage2fivegrade(105));
            assertEquals("错误",MyUtil.percentage2fivegrade(-55));
        }
    
        @Test
        public void testBoundary(){
            assertEquals("不及格",MyUtil.percentage2fivegrade(0));
            assertEquals("及格",MyUtil.percentage2fivegrade(60));
            assertEquals("中等",MyUtil.percentage2fivegrade(70));
            assertEquals("良好",MyUtil.percentage2fivegrade(80));
            assertEquals("优秀",MyUtil.percentage2fivegrade(90));
            assertEquals("优秀",MyUtil.percentage2fivegrade(100));
        }
    }
    

    以上测试代码中的testNormal()方法用于测试一般情况,而testException()和testBoundary()则是对意外情况和边界进行测试。如果三种测试中的一种没有通过,IDEA会提示是哪一块出错,以及出了什么错误:

    我们可以从图中看出是边缘测试没有通过,原本应该显示“优秀”的显示了“错误”。于是回去看MyUtil类:

    发现是设置“优秀”的范围时没有包含100分(我刚刚改的),所以100分便被归到了“错误”中。修改以后再次运行MyUtilTest,可看到三个测试都通过:

    由此可看出,除了对普通数据的测试之外,边界情况和非法情况的测试也十分重要。

    以TDD的方式研究学习StringBuffer

    在老师的博客《积极主动敲代码,使用JUnit学习Java》中,给出了一个用于学习StringBuffer几个方法的程序:

    public class StringBufferDemo{        
       public static void main(String [] args){    
           StringBuffer buffer = new StringBuffer();    
           buffer.append('S');     
           buffer.append("tringBuffer");     
           System.out.println(buffer.charAt(1));     
           System.out.println(buffer.capacity();     
           System.out.println(buffer.indexOf("tring"));    
           System.out.println("buffer = " + buffer.toString());    
      }    
    }  
    

    了解这几个方法以后,选择了charAt(),length(),capacity()这三个方法,开始撰写产品代码:

    public class StringBufferDemo {
        StringBuffer buffer=new StringBuffer();
        public Character charAt(int i){
            return buffer.charAt(i);
        }
        public int length(){
            return buffer.length();
        }
        public int capacity(){
            return buffer.capacity();
        }
    }
    

    接着是撰写测试代码,来测试这三个方法是否正确:

    import junit.framework.TestCase;
    import org.junit.Test;
    
    public class StringBufferDemoTest extends TestCase {
        StringBuffer a=new StringBuffer("yesterday");
        StringBuffer b=new StringBuffer("tomorrowisnotnow");
        StringBuffer c=new StringBuffer("liveherenowtodayisneccessary");
        
        @Test
        public void testcharAt(){
            assertEquals('y',a.charAt(0));
            assertEquals('r',b.charAt(5));
            assertEquals('n',c.charAt(8));
        }
        
        @Test
        public void testlength(){
            assertEquals(9,a.length());
            assertEquals(16,b.length());
            assertEquals(28,c.length());
        }
        
        @Test
        public void testcapacity(){
            assertEquals(25,a.capacity());
            assertEquals(32,b.capacity());
            assertEquals(44,c.capacity());
        }
    }
    

    在测试代码中,我设定了三个长度逐渐增加的字符串。运行后,显示测试成功。

    以TDD的方式开发一个复数类Complex

    要求:定义属性并生成getter,setter;定义构造函数;定义公有方法(加减乘除)。

    测试代码:

    import junit.framework.TestCase;
    import org.junit.Test;
    
    public class ComplexTest extends TestCase {
        Complex a=new Complex(1,2);
        Complex b=new Complex(-2,-1);
        Complex c=new Complex(4,-2);
        Complex d=new Complex(4,-2);
        @Test
        public void testequals(){
            assertEquals(false,a.equals(b));
            assertEquals(false,b.equals(c));
            assertEquals(true,c.equals(d));
        }
    
        @Test
        public void testAdd(){
            assertEquals(new Complex(-1,1),a.ComplexAdd(b));
            assertEquals(new Complex(5,0),a.ComplexAdd(c));
        }
    
        @Test
        public void testSub(){
            assertEquals(new Complex(3,3),a.ComplexSub(b));
            assertEquals(new Complex(-3,4),a.ComplexSub(c));
        }
    
        @Test
        public void testMulti(){
            assertEquals(new Complex(0,-5),a.ComplexMulti(b));
            assertEquals(new Complex(8,6),a.ComplexMulti(c));
        }
    
        @Test
        public void testDiv(){
            assertEquals(new Complex(0,0.5),a.ComplexDiv(c));
            assertEquals(new Complex(-0.3,-0.4),b.ComplexDiv(c));
        }
    }
    

    产品代码:

    public class Complex {
        // 定义属性并生成getter,setter
        private double r;
        private double i;
        // 定义构造函数
        public Complex(double r,double i){
            this.r=r;
            this.i=i;
        }
        public static double getRealPart(double r){
            return r;
        }
        public static double getImagePart(double i){
            return i;
        }
    
        //Override Object
        public boolean equals(Object obj){
    
            Complex complex=(Complex) obj;
            if (complex.r!=r) {
                return false;
            }
            if(complex.i!=i){
                return false;
            }
            return true;
        }
        public String toString(){
            String str=new String();
            if (i==0) str=r+"";
            else if(i<0) str=r + ""+i+"i";
            else str=r+""+"+"+i+"i";
            return str;
        }
    
        // 定义公有方法:加减乘除
        Complex ComplexAdd(Complex a){
            return new Complex(r+a.r,i+a.i);
        }
        Complex ComplexSub(Complex a){
            return new Complex(r-a.r,i-a.i);
        }
        Complex ComplexMulti(Complex a){
            return new Complex(r*a.r-i*a.i,r*a.i+i*a.r);
        }
        Complex ComplexDiv(Complex a){
            return new Complex((r*a.r+i*a.i)/(a.r*a.r+a.i*a.i),(i*a.r-r*a.i)/(a.r*a.r+a.i*a.i));
        }
    }
    

    测试成功截图:

    二、面向对象三要素

    面向对象的三要素是封装、继承与多态。为借助抽象思维用好三要素:SOLID原则给出了指导。

    • SRP,单一职责原则;

    • OCP,开放-封闭原则;

    • LSP,替换原则;

    • ISP,接口分离原则;

    • DIP,依赖倒置原则。

    练习,体会OCP原则和DIP原则的应用

    老师给出的设计模式示例代码如下:

    // Server Classes 
    abstract class Data { 
        abstract public void DisplayValue(); 
    }
    class Integer extends  Data {    
        int value; 
        Integer() {
             value=100; 
        }  
        public void DisplayValue(){
            System.out.println (value);
        } 
     } 
    // Pattern Classes 
    abstract class Factory { 
       abstract public Data CreateDataObject(); 
    }
    class IntFactory extends Factory { 
       public Data CreateDataObject(){
         return new Integer(); 
       } 
    } 
    //Client classes 
    class Document {    
        Data pd; 
        Document(Factory pf){ 
           pd = pf.CreateDataObject(); 
        } 
        public void DisplayData(){
           pd.DisplayValue(); 
       } 
     } 
     //Test class
     public class MyDoc {
        static Document d;
        public static void main(String[] args) {
                d = new Document(new IntFactory()); 
                d.DisplayData(); 
        }   
    }
    

    用我的学号(28)模6取余,得到4,须根据以下结果进行代码扩充:让系统支持Float类,并在MyDoc类中添加测试代码表明添加正确。

        abstract class Data {
            abstract public void DisplayValue();
        }
        class Integer extends  Data {
            int value;
            Integer() {
                value=100;
            }
            public void DisplayValue(){
                System.out.println (value);
            }
        }
        class Float extends Data{
            float value;
            Float(){
                value=20155328;
            }
            public void DisplayValue(){
                System.out.println(value);
            }
        }
        // Pattern Classes
        abstract class Factory {
            abstract public Data CreateDataObject();
        }
        class IntFactory extends Factory {
            public Data CreateDataObject(){
                return new Integer();
            }
        }
        class FloatFactory extends Factory{
            public Data CreateDataObject(){
                return new Float();
            }
        }
    

    测试代码如下:

        //Test class
        public class MyDoc {
            static Document d;
            public static void main(String[] args) {
                d = new Document(new FloatFactory());
                d.DisplayData();
            }
        }
    

    使用StarUML进行建模,画类图

    三、实验中遇到的问题及解决方式

    • 问题1:对StringBuffer的capacity()方法理解不太清晰。

    • 解决方法:参考StringBuffer的Capacity详解及实践,知StringBuffer的默认大小是16,即如果长度小于16则默认容量为16,当StringBuffer达到最大容量时,会将最大容量增加到原来容量的两倍再加2.

    • 问题2:在测试Complex类时,出现了这样的red bar:

    • 解决方法:由图可知是equals()方法的撰写出现了问题,于是回到Complex中重新撰写如下:
        public boolean equals(Object obj){
    
            Complex complex=(Complex) obj;
            if (complex.r!=r) {
                return false;
            }
            if(complex.i!=i){
                return false;
            }
            return true;
        }
    

    然后再次测试,显示测试成功。

    四、实验体会

    相比起第一次实验,觉得这次实验让自己受益匪浅。在实验课前花了不少的时间跟着老师的step by step教程慢慢学习,体会到了单元测试的实用性。以前写代码就算会自己带一些测试之类的东西来看看是否正确,但一旦结果出错也需要耗费较长的时间去找出错误,而单元测试就可以通过red bar和错误提示很快找到错误源头,然后解决。希望自己以后可以更好更熟练的掌握TDD编程方法吧。

  • 相关阅读:
    Hibernate session.save()实体类,主键增长问题
    安装原版 Windows 7 后需要安装的微软更新 和 必备系统组件
    如何使用站群程序来批量建网站?
    Android图片优化指南
    SQLite为何要用C语言来开发?
    Kotlin使用率达35%,Java要退位了?
    JavaScript的几种循环使用方式及性能解析
    30 分钟理解 CORB 是什么
    PHP 字符串相关常识
    那些10w+的公众号都在写什么?
  • 原文地址:https://www.cnblogs.com/zyqzyq/p/6747415.html
Copyright © 2020-2023  润新知