不会太深入这个话题(许多书和网站都讨论),有一个称作 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
- 附加部分