-初步掌握單元測試和TDDjava
1.三種代碼:僞代碼、產品代碼、測試代碼。
咱們應該先寫僞代碼理清思路->再用特定編程語言翻譯成產品代碼->最後寫測試代碼,驗證本身的代碼有沒有問題。
(1)僞代碼編程
百分制轉五分制: 若是成績小於60,轉成「不及格」 若是成績在60與70之間,轉成「及格」 若是成績在70與80之間,轉成「中等」 若是成績在80與90之間,轉成「良好」 若是成績在90與100之間,轉成「優秀」 其餘,轉成「錯誤」
(2)產品代碼設計模式
public class MyUtil{ public static String percentage2fivegrade(int grade){ //若是成績小於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 "錯誤"; } }
(3)測試代碼安全
用50分測試時:編程語言
public class MyUtilTest { public static void main(String[] args) { // 百分制成績是50時應該返回五級制的「不及格」 if(MyUtil.percentage2fivegrade(50) != "不及格") System.out.println("test failed!"); else System.out.println("test passed!"); } }
輸入負分或大於100時:ide
public class MyUtilTest3 { public static void main(String[] args) { //測試出錯狀況 if(MyUtil2.percentage2fivegrade(-10) != "錯誤") System.out.println("test failed 1!"); else if(MyUtil2.percentage2fivegrade(115) != "錯誤") System.out.println("test failed 2!"); else System.out.println("test passed!"); } }
測試邊界狀況:函數
public class MyUtilTest4{ public static void main(String[] args) { //測試邊界狀況 if(MyUtil2.percentage2fivegrade(0) != "不及格") System.out.println("test failed 1!"); else if(MyUtil2.percentage2fivegrade(60) != "及格") System.out.println("test failed 2!"); else if(MyUtil2.percentage2fivegrade(70) != "中等") System.out.println("test failed 3!"); else if(MyUtil2.percentage2fivegrade(80) != "良好") System.out.println("test failed 4!"); else if(MyUtil2.percentage2fivegrade(90) != "優秀") System.out.println("test failed 5!"); else if(MyUtil3.percentage2fivegrade(100) != "優秀") System.out.println("test failed 6!"); else System.out.println("test passed!"); } }
改正錯誤後最後代碼:單元測試
public class MyUtil{ public static String percentage2fivegrade(int grade){ //若是成績小於0,轉成「錯誤」 if ((grade < 0)) return "錯誤"; //若是成績小於60,轉成「不及格」 else 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 "優秀"; //若是成績大於100,轉成「錯誤」 else return "錯誤"; } }
步驟以下:測試
概念:先寫測試代碼,而後再寫產品代碼的開發方法
通常步驟以下:優化
明確當前要完成的功能,記錄成一個測試列表
快速完成編寫針對此功能的測試用例
測試代碼編譯不經過(沒產品代碼呢)
編寫產品代碼
測試經過
對代碼進行重構,並保證測試經過(重構下次實驗練習)
循環完成全部功能的開發
TDD模式測試代碼的編寫和調試結果
測試代碼以下(注意@Test)
public class StringBufferDemoTest extends TestCase { StringBuffer a = new StringBuffer("StringBuffer");//測試12個字符(<=16) StringBuffer b = new StringBuffer("StringBufferStringBuffer");//測試24個字符(>16&&<=34) StringBuffer c = new StringBuffer("StringBufferStringBufferStringBuffer");//測試36個字符(>=34) @Test public void testcharAt() throws Exception{ assertEquals('S',a.charAt(0)); assertEquals('g',a.charAt(5)); assertEquals('r',a.charAt(11)); } @Test public void testcapacity() throws Exception{ assertEquals(28,a.capacity()); assertEquals(40,b.capacity()); assertEquals(52,c.capacity()); } @Test public void testlength() throws Exception{ assertEquals(12,a.length()); assertEquals(24,b.length()); assertEquals(36,c.length()); } @Test public void testindexOf() throws Exception{ assertEquals(0,a.indexOf("Str")); assertEquals(5,a.indexOf("gBu")); assertEquals(10,a.indexOf("er")); } }
結果截圖
面向對象三要素
1.抽象:去粗取精、化繁爲簡、由表及裏、異中求同。在抽象的最高層,可使用問題環境的語言,以歸納的方式敘述問題的解;在抽象的較低層,則採用過程化的方式進行描述。
2.面向對象(Object-Oriented)的三要素包括:封裝、繼承、多態。面向對象的思想涉及到軟件開發的各個方面,如面向對象分析(OOA)、面向對象設計(OOD)、面向對象編程實現(OOP)。
3.繼承
以封裝爲基礎,一個類的定義能夠基於另一個已經存在的類,即子類基於父類,從而實現父類代碼的重用。其更爲普遍而重要的做用是實現多態。
示例:Dog類和Cat類都有Color屬性和相應的setter和getter方法,能夠經過繼承使其精煉化,把Color屬性和相應的setter和getter方法放到父類Animal中。
UML類圖要展現類之間的靜態關係,UML中依賴用帶箭頭的直線表示,例如上例中的Animal類依賴Cat類和Dog類
UML類圖中繼承的表示法,是用一個帶三角的直線指向父類,例如上例中Cat類和Dog類繼承Animal類,消除了Dog類和Cat類中的重複代碼,符合DRY的要求
設計模式初步
SRP(Single Responsibility Principle,單一職責原則)
OCP(Open-Closed Principle,開放-封閉原則)
LSP(Liskov Substitusion Principle,Liskov替換原則)
ISP(Interface Segregation Principle,接口分離原則)
DIP(Dependency Inversion Principle,依賴倒置原則)
2.模式與設計模式:模式是某外在環境(Context) 下﹐對特定問題(Problem)的慣用解決之道(Solution)。計算機科學中有不少模式:
GRASP模式
分析模式
軟件體系結構模式
設計模式:建立型,結構型,行爲型
管理模式: The Manager Pool 實現模式
界面設計交互模式
3.設計模式實示例:設計模式(design pattern)提供一個用於細化軟件系統的子系統或組件,或它們之間的關係圖,它描述通訊組件的公共再現結構,通訊組件能夠解決特定語境中的一個設計問題。設計模式有四個基本要素:
示例:
import java.util.Objects; abstract class Data { abstract public void DisplayValue(); } class Integer extends Data { int value; Integer() { value=100; } public void DisplayValue(){ System.out.println (value); } } class Long extends Data { long value; Long() { value=2147483647; } public void DisplayValue(){ System.out.println (value); } } class Float extends Data { float value; Float() { value=(float)20165322.0; } 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 LongFactory extends Factory { public Data CreateDataObject(){ return new Long(); } } class FloatFactory extends Factory { public Data CreateDataObject(){ return new Float(); } } //Client classes class Document { Data data; Document(Factory factory){ data = factory.CreateDataObject(); } public void DisplayData(){ data.DisplayValue(); } } public class MyDoc { static Document d; MyDoc(Document d) { this.d = d; } public static void main(String[] args) { d = new Document(new FloatFactory()); d.DisplayData(); } } }
運行結果
使用TDD的方式設計關實現複數類Complex
僞代碼
1)複數類ComplexNumber的屬性 realPart: 實部,表明複數的實數部分 imaginPart: 虛部,表明複數的虛數部分 2)複數類ComplexNumber的方法 ComplexNumber() 構造函數,將實部,虛部都置爲0 ComplexNumber(double realPart, double imaginPart) 構造函數,建立複數對象的同時完成複數的實部,虛部的初始化 getRealPart() 獲取實部 getImaginaryPart() 獲取虛部 getRealPart(double realPart) 設置實部 getImaginaryPart(double imaginPart) 設置虛部 ComplexAdd(ComplexNumber c) 複數相加 ComplexAdd(double realPart2) 複數相加 ComplexMinus(ComplexNumber c) 複數相減 ComplexMinus(double realPart2) 複數相減 ComplexMulti(ComplexNumber c) 複數相乘 ComplexMulti(double realPart2) 複數相乘 toString() 把當前複數對象的實部,虛部組合成a+bi的字符串形式
測試代碼
import static junit.framework.Assert.*; import junit.framework.TestCase; import org.junit.Test; public class ComplexNumberTest extends TestCase { ComplexNumber c1 = new ComplexNumber(3,5); ComplexNumber c2 = new ComplexNumber(3,5); double a = 5; @Test public void testAdd1() throws Exception { c1.ComplexAdd(c2); assertEquals(6.0, c1.getRealPart()); assertEquals(10.0, c1.getImaginPart()); } @Test public void testAdd2() throws Exception { c1.ComplexAdd(a); assertEquals(8.0, c1.getRealPart()); assertEquals(5.0, c1.getImaginPart()); } @Test public void testMinus1() throws Exception { c1.ComplexMinus(c2); assertEquals(0.0, c1.getRealPart()); assertEquals(0.0, c1.getImaginPart()); } public void testMinus2() throws Exception { c1.ComplexMinus(a); assertEquals(-2.0, c1.getRealPart()); assertEquals(5.0, c1.getImaginPart()); } @Test public void testMulti1() throws Exception { c1.ComplexMulti(c2); assertEquals(9.0, c1.getRealPart()); assertEquals(25.0, c1.getImaginPart()); } public void testMulti2() throws Exception { c1.ComplexMulti(a); assertEquals(15.0, c1.getRealPart()); assertEquals(5.0, c1.getImaginPart()); } }
產品代碼
public class ComplexNumber { private double realPart; private double imaginPart; public ComplexNumber(){ this.realPart = 0.0.; this.imaginPart = 0.0; } public ComplexNumber(double r, double i){ this.realPart = r; this.imaginPart = i; } public double getRealPart(){ return realPart; } public double getImaginPart(){ return imaginPart; } public void setRealPart(double d){ this.realPart = d; } public void setImaginPart(double d){ this.imaginPart = d; } public void ComplexAdd(ComplexNumber c){ this.realPart += c.realPart; this.imaginPart += c.imaginPart; } public void ComplexAdd(double c){ this.realPart += c; } public void ComplexMinus(ComplexNumber c){ this.realPart -= c.realPart; this.imaginPart -= c.imaginPart; } public void ComplexMinus(double c){ this.realPart -= c; } public void ComplexMulti(ComplexNumber c){ this.realPart = c.realPart; this.imaginPart = c.imaginPart; } public void ComplexMulti(double c){ this.realPart *= c; } @Override public String toString(){ return String.format("%f + %fi", this.getRealPart(),this.getImaginPart()); } }
結果截圖
TDD
僞代碼(思路)→ 測試代碼(產品預期功能)→ 產品代碼(實現預期功能)
總結單元測試的好處
(1)對於整個項目來講,有了完整的測試,保證項目最後交付測試有了可靠依據,減小後期維護的精力和費用
(2)對於開發人員來講大大減小調試工做的時間,同時也規範了對於代碼安全管理
S.O.L.I.D類設計原則:
SRP(Single Responsibility Principle,單一職責原則)
OCP(Open-Closed Principle,開放-封閉原則)
LSP(Liskov Substitusion Principle,Liskov替換原則)
ISP(Interface Segregation Principle,接口分離原則)
DIP(Dependency Inversion Principle,依賴倒置原則)
(3)可以有效優化代碼的設計
(4)測試自己是被測代碼的用法說明,替代了一部分代碼功能,迫使本身要將被測代碼設計得更加獨立地去完成某個或某幾個功能
OCP實現:
(1)抽象和繼承,(2)面向接口編程