不會太深入這個話題(許多書和網站都討論),有一個稱作 TDD(測試驅動開發)的技術,主張一個好的實踐是在方法實現之前編寫測試。該過程分為以下幾個步驟:
* 編寫一個測試
* 編譯器運行的最小化必須代碼添加到類的定義中,代碼不必在這個階段工作。編譯器只要產生一個輸出就夠了。
* 運行測試,將很可能失敗因為模型方法還沒有真的被實現。
* 修改代碼以提供最小需求使測試可以通過
* 運行測試并通過
* 代碼重構;如果修改引起了一個 bug ,一個測試將會報告故障
然后重復這個過程直到所有應用需要的功能都被實現。編寫最少的代碼來通過測試確保應用不會假裝比嚴格需求做更多的事情。這個概念是在兩個縮寫詞中重新獲得,KISS(keep it simple,stupid),和YAGNI(you ain't gonna need it,你不會需要它)。
雖然描述的每個步驟非常小,但它取決于現實的需要跟程序員的個人態度。
在下面的例子中,這個技術會被在第一個步驟采用,并具有一定程度的自由,不然讀者繁瑣。它將被在一個簡單的驗證框架基礎上實現。所需要的是一個非常簡單的對象,執行一個驗證并在驗證OK的時候返回true 反之返回false;后者的情況下,一個錯誤信息將被生成。
所以我們開始第一個測試類。 TestERegValidator 繼承 TestCase。通過測試類在以它們所要測試的類名稱來命名。第一個驗證類接受一個文本值和一個正則表達式模式來執行驗證。
~~~
class TestERegValidator extends haxe.unit.TestCase
{
public function testValidateFalse()
{
var v = new ERegValidator(“test”, “x”, null);
assertFalse(v.validate());
}
}
~~~
記住,正則表達式在 Flash 9 版本之前沒有出現。
在測試中,一個 ERegValidator 類的實例被初始化。第一個測試會檢查一個字符串值是否匹配模式 x;這通過 assertFalse 來描述,如果傳遞的參數為 true 則會失敗。測試類和第一個測試準備好了,但是明顯的,這段代碼本身不能被編譯,因為類 ERegValidator 還不存在。
~~~
class ERegValidator
{
public function new(value : String, pattern : String, opt : String) {}
public function validate() : Bool
{
return true;
}
}
~~~
現在代碼準好被編譯了;但是一個最后的事情漏掉了,還需要一個入口類 和一個 main 方法來運行測試并查看結果。
~~~
class Main
{
static function main()
{
var runner = new haxe.unit.TestRunner();
runner.add(new TestERegValidator());
runner.run();
}
}
~~~
要讓這段代碼在所有的三個平臺工作,下面的 .hxml 文件用來編譯。在編譯單元測試時打開 -debug 開關尤為重要,因為在失敗的情況下更容易發現和糾正錯誤。
~~~
# Neko
-neko main.n
-main Main
-debug
--next
# Flash9
-swf main.swf
-swf-version 9
-main Main
-debug
--next
# Javascript
-js main.js
-main Main
-debug
~~~
編譯結束會在每個平臺產生同樣的結果。
~~~
Class: TestERegValidator F
* TestERegValidator::testValidateFalse()
ERR: Main.hx:12(TestERegValidator.testValidate) - expected false but was true
// here are omitted some lines of debugging information
// that vary with the different platforms
FAILED 1 tests, 1 failed, 0 success
~~~
這是預期的結果;代碼編譯但是并沒有工作,因為驗證函數總是返回 true ,而斷言期待一個 false 參數。因此,只有測試需要的代碼被引入:
~~~
class ERegValidator
{
public function new(value:String, pattern:String, ?opt:String) {}
public function validate() : Bool
{
return false;
}
}
~~~
現在執行代碼會報告:
~~~
Class: TestERegValidator .
OK 1 tests, 0 failed, 1 success
~~~
一個新的失敗的測試被引入:
~~~
public function testValidateTrue()
{
var v = new ERegValidator(“test”, “t”, null);
assertTrue(v.validate());
}
~~~
這次測試運行兩個,其中一個會失敗。是時候調整代碼來使兩個測試驗證可能會通過。
~~~
class ERegValidator
{
private var value : String;
private var pattern : String;
private var opt : String;
public function new(value : String, pattern : String, ?opt : String)
{
this.value = value;
this.pattern = pattern;
this.opt = if(opt == null) “” else opt;
}
public function validate() : Bool
{
var er = new EReg(pattern, opt);
return er.match(value);
}
}
~~~
構造器參數現在保存在實例,可以有效的用于驗證。現在,失敗的情況下,一個錯誤需要提供來通知用戶,一些故障阻止了驗證過程成功完成。新的測試被添加:
~~~
public function testEmptyError()
{
var v = new ERegValidator(“test”, “t”);
v.validate();
assertEquals(v.error, null);
}
~~~
需要類中一個新的變量 error,在成功驗證的情況下,它必須有一個 null 值。
~~~
public var error(default, null) : String;
~~~
然后開始另一個測試。
創建測試和實現代碼的過程會被重復很多次直到所有所需功能都被實現。所有這些迭代的結果是類ERegValidator的完成;一個新的類 EmailValidator 也被引入,通過它的對應的測試類 TestEmailValidator。
~~~
class TestEmailValidator extends haxe.unit.TestCase
{
public function testConventional()
{
var v = new EmailValidator(“john@example.com”);
assertTrue(v.validate());
}
public function testDirty()
{
var v = new EmailValidator(“john @example.com”); // spaces are not allowed
assertFalse(v.validate());
}
public function testIncomplete()
{
var v = new EmailValidator(“john”);
assertFalse(v.validate());
}
public function testDoubleDotted()
{
var v = new EmailValidator(“john@example..com”);
assertFalse(v.validate());
}
}
class TestERegValidator extends haxe.unit.TestCase
{
public function testValidateTrue()
{
var v = new ERegValidator(“test”, “t”);
assertTrue(v.validate());
}
public function testValidateFalse()
{
var v = new ERegValidator(“test”, “x”);
assertFalse(v.validate());
}
public function testEmptyError()
{
var v = new ERegValidator(“test”, “t”);
v.validate();
assertTrue(v.error == null);
}
public function testNotEmptyError()
{
var v = new ERegValidator(“test”, “x”);
v.validate();
assertTrue(v.error != null);
}
public function testErrorContent()
{
var value = “test”;
var pattern = “x”;
var v = new ERegValidator(value, pattern);
v.validate();
assertTrue(v.error.indexOf(value) > = 0);
assertTrue(v.error.indexOf(pattern) > = 0);
}
}
class EmailValidator extends ERegValidator
{
public function new (email : String)
{
super(email, “^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$”, “i”);
}
}
class ERegValidator
{
public var error(default, null) : String;
private var value : String;
private var pattern : String;
private var opt : String;
public function new(value : String, pattern : String, ?opt : String)
{
this.value = value;
this.pattern = pattern;
this.opt = if(opt == null) “” else opt;
}
public function validate() : Bool
{
var er = new EReg(pattern, opt);
if(er.match(value))
return true;
else {
error = “’”+value+”’ does not match the expression /”+pattern+”/”;
return false;
}
}
}
~~~
框架可以被繼承,和添加新的數值范圍,信用卡號碼,電話號碼等等的驗證類。一個公共的接口 Validator 可以被引入來使這些類交換,可能改變 error 變量類型從 String 為 List<String>是一個好主意。這個方式一個 獨立的 Validator 可以通知更多的錯誤信息。一個一般的 ValidatorGroup 類可以引入來一次性執行許多鏈式驗證。
- 本書目錄
- 第一章:Haxe介紹
- 互聯網開發的一個問題
- Haxe是什么,為什么產生
- Haxe編譯工具
- Haxe語言
- Haxe如何工作
- 那么Neko是什么
- Haxe和Neko的必須條件
- 本章摘要
- 第二章:安裝、使用Haxe和Neko
- 安裝Haxe
- 使用Haxe安裝程序
- 在Windows上手動安裝Haxe
- Linux上手動安裝Haxe
- 安裝Neko
- Windows上手動安裝Neko
- 在Linux上安裝Neko
- Hello world! 一式三份
- 編譯你的第一個Haxe應用
- 你的程序如何編譯
- HXML編譯文件
- 編譯到Neko
- 編譯為JavaScript
- 程序結構
- 編譯工具開關
- 本章摘要
- 第三章:基礎知識學習
- Haxe層級結構
- 標準數據類型
- 變量
- 類型推斷
- 常數變量
- 簡單的值類型
- 浮點類型
- 整型
- 選擇數值類型
- 布爾類型
- 字符串類型
- 抽象類型
- Void 和 Null
- 動態類型
- unknown類型
- 使用untyped繞過靜態類型
- 注釋代碼
- 轉換數據類型
- Haxe數組
- Array
- List
- Map
- Haxe中使用日期時間
- 創建一個時間對象
- Date組件
- DateTools類
- 操作數據
- 操作符
- Math類
- 使用String函數
- 本章摘要
- 第四章:信息流控制
- 數據存放之外
- 條件語句
- if語句
- switch語句
- 從條件語句返回值
- 循環
- while循環
- for循環
- 循環集合
- Break和Continue
- 函數
- 類的函數
- 局部函數
- Lambda類
- 本章摘要
- 第五章:深入面向對象編程
- 類和對象
- 實例字段
- 靜態字段
- 理解繼承
- Super
- 函數重載
- 構造器重載
- toString()
- 抽象類和抽象方法
- 靜態字段,實例變量和繼承
- 繼承規則
- 使用接口
- 高級類和對象特性
- 類的實現
- 類型參數
- 匿名對象
- 實現動態
- Typedef
- 擴展
- 枚舉
- 構造器參數
- 本章摘要
- 第六章:組織你的代碼
- 編寫可重用代碼
- 使用包
- 聲明一個包
- 隱式導入
- 顯式導入
- 枚舉和包
- 類型查找順序
- 導入一個完整的包
- 導入庫
- Haxe標準庫
- Haxelib庫
- 其他項目中的庫
- 外部庫
- 使用資源
- 文檔化代碼
- 離線文檔
- 在線文檔
- 單元測試
- haxe.unit包
- 編寫測試
- 本章摘要
- 第七章:錯誤調試
- trace函數
- trace輸出
- haxe的trace和ActionScript的trace
- 異常
- 異常處理
- CallStack和ExceptionStack
- 異常管理類
- 創建完全的異常處理類
- 異常類代碼
- 本章摘要
- 第八章:跨平臺工具
- XML
- XML剖析
- Haxe XML API
- 正則表達式
- EReg類
- 模式
- 定時器
- 延遲動作
- 隊列動作
- MD5
- 本章摘要
- 第九章:使用Haxe構建網站
- Web開發介紹
- Web 服務器
- 使用Web服務器發布內容
- HTML速成課程
- Haxe和HTML的區別
- NekoTools Web Server
- Apache安裝mod_neko
- Windows安裝Apache和mod_neko
- Linux安裝Apache和Mod_Neko
- 第一個Haxe網站
- 使用Neko作為網頁Controller
- neko.Web類
- Neko作為前端控制器
- 本章摘要
- 第十章:使用模板進行分離式設計
- 什么是模板
- Template類
- Template語法
- 使用資產
- 何時在模板中使用代碼
- 服務器端模板的Templo
- 安裝Templo
- 使用Templo
- haxe.Template和mtwin.Templo表達式上的區別
- Attr表達式
- Raw表達式
- 邏輯表達式
- 循環表達式
- set, fill, 和 use表達式
- Templo中使用宏
- 手動編譯模版
- 第十一章:執行服務端技巧
- 第十二章:使用Flash構建交互內容
- 第十三章:使用IDE
- 第十四章:通過JavaScript制作更多交互內容
- 第十五章:通過Haxe遠程通信連接所學
- 第十六章:Haxe高級話題
- 第十七章:Neko開發桌面應用
- 第十八章:用SWHX開發桌面Flash
- 第十九章:多媒體和Neko
- 第二十章:使用C/C++擴展Haxe
- 附加部分