RSS
 

Archive for the ‘开发心得’ Category

QUnit, 一款JavaScript单元测试框架

12 Oct

QUnit是jQuery支持的一款JavaScript单页测试框架。简单易用。可以从QUnit的Github页面获取其代码。QUnit的使用与JUnit类似,相信有JUnit经验的开发者和测试人员可以很快上手。

开始写测试用例

  • test( name, expected, test )
    一个常规的测试用例。test里是测试用例的具体内容。
  • asyncTest( name, expected, test )
    一个异步测试用例。默认的test都是同步的。内容中必须显示地调用start(),测试才会开始。
  • expect( amount )
    用于测试代码中,表示在本测试里期待会执行amount个断言,大于或少于这个数量,测试都将失败。
  • module( name, lifecycle )
    定义一个名为name的模块。在可选参数lifecycle中,可以定义模块开始和结束的测试内容。具体见文档。
  • QUnit.init( )
    启动QUnit测试。如果测试在进行中,则会重新启动。基本不用。

QUnit支持的断言

  1. ok( state, message )
    真假断言,state为true则通过。类似于JUnit的assertTrue。
  2. equal( actual, expected, message )
    相等断言,actual和expected相等(==)则通过。类似于JUnit的assertEquals。
  3. notEqual( actual, expected, message )
    不等断言,actual和expected不相等(!=)则通过。类似于JUnit的assertNotEquals。
  4. deepEqual( actual, expected, message )
    递归相等断言,actual和expected全相等(包括其子元素都相等,适用于基本类型,数组和对象)则通过。对于基本类型,相当于strictEqual。
  5. notDeepEqual( actual, expected, message )
    递归不相等断言,actual和expected不全相等(包括其子元素都相等,适用于基本类型,数组和对象)则通过。对于基本类型,相当于notStrictEqual。
  6. strictEqual( actual, expected, message )
    全相等断言,actual和expected全相等(===)则通过。
  7. notStrictEqual( actual, expected, message )
    不全相等断言,actual和expected不全相等(===)则通过。
  8. raises( block, expected, message )
    异常断言,block中抛出异常则通过,expected为可选参数,是所期待抛出异常名的正则表达式。

把QUnit集成到现有工具

QUnit在执行的过程中会调用一系列函数,告知外界运行状况,大家可以覆盖这些函数,达到集成的目的。

  • QUnit.log({ result, actual, expected, message })
    每当断言执行结束后会调用此函数。
  • QUnit.testStart({ name })
    每当一个测试执行开始时会调用此函数。
  • QUnit.testDone({ name, failed, passed, total })
    每当一个测试执行结束后会调用此函数。
  • QUnit.moduleStart({ name })
    每当一个模块执行开始时会调用此函数。
  • QUnit.moduleDone({ name, failed, passed, total })
    每当一个模块执行结束后会调用此函数。
  • QUnit.begin()
    当QUnit开始时会调用此函数。
  • QUnit.done()
    当QUnit结束后会调用此函数。

在后面的实例中,我写了一些覆盖,打印了执行过程。相信有了这些回调函数的帮助,写一个进度条出来也不是难事。

QUnit的过滤器

  • noglobals
    如果勾选,那么如果一个测试中引入了全局变量,则测试会失败。JavaScript中引入全局变量(window的属性)是不推荐的行为。
  • notrycatch
    如果勾选,代码中的try-catch将不会生效,一旦程序中有异常发生,测试即刻终止。

QUnit的一个实例

页面输出如下:

页面全部代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<script src="http://code.jquery.com/jquery-latest.js"></script>
		<link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css" type="text/css" media="screen" />
		<style type="text/css">
			#qunit-logs li {
				font-size: small;
				color: #366097;
			}
		</style>

		<script type="text/javascript" src="http://code.jquery.com/qunit/git/qunit.js"></script>

		<script>
			$(document).ready(function() {
				var log = function(message) {
					$('#qunit-logs').append('<li>[' + (new Date()).toLocaleTimeString() + ' ' +  (new Date()).getMilliseconds() + ']' + message + '</li>');
				}

				QUnit.begin = function() {
					log('[begin]');
				}

				QUnit.done = function() {
					log('[done]');
				}

				QUnit.reset = function() {
					log('[reset]');
				}

				QUnit.testStart = function(o) {
					log('[testStart]: ' + o.name);
				}

				QUnit.testDone = function(o) {
					log('[testDone]: ' + o.name + ', total=' + o.total + ', passed=' + o.passed + ', failed=' + o.failed);
				}

				QUnit.moduleStart = function(o) {
					log('[moduleStart]: ' + o.name);
				}

				QUnit.moduleDone = function(o) {
					log('[moduleDone]: ' + o.name + ', total=' + o.total + ', passed=' + o.passed + ', failed=' + o.failed);
				}

				test("a basic test example", function() {
					ok( true, "this test is fine" );
					var value = "hello";
					equal( value, "hello", "We expect value to be hello" );
				});

				module("Module A");

				test("first test within module", function() {
  					ok( true, "all pass" );
				});

				module("Module C");

				test("second test within module", function() {
					ok( true, "all pass" );
				});

				module("Module B");

				test("some other test", function() {
					//expect(1);
					equal( true, false, "failing test" );
					equal( true, true, "passing test" );
				});

				module("Module A");

				test("second test within module", function() {
					ok( true, "all pass" );
				});
			});
		</script>
	</head>
	<body>
		<h1 id="qunit-header">QUnit example</h1>
		<h2 id="qunit-banner"></h2>
		<div id="qunit-testrunner-toolbar"></div>
		<h2 id="qunit-userAgent"></h2>
		<ol id="qunit-tests"></ol>
		<div id="qunit-fixture">test markup, will be hidden</div>
		<ul id="qunit-logs"></ul>
	</body>
</html>
 
 

折腾:Macbook Pro装SSD及光驱位改装硬盘盒

29 Aug

今天订的货终于到齐了。折腾一下,很顺利,记录如下:
我的目标:给Macbook装SSD,同时把原来的硬盘装在Macbook光驱位置,原来的光驱装在外置光盘盒里。

1. 拆后盖:

(1) 准备合适的家伙,后盖的螺丝虽然是十字的,但是比较细小,换内存换硬盘你会频繁打开它们,要保护好。
(2) 经常用的Macbook,环境状况不好的话,里面会有超级多的灰,打开前要有心理准备。
(3) 我把螺丝按拆下来位置摆成这样了。

2. 拆光驱:

(1) 到底把SSD盘装在原来的硬盘位,还是Optibay光驱位呢?建议装在Optibay位,因为Macbook硬盘位是有跌落保护的,因此在这里装机械硬盘能起到保护的效果。我这里是因为之前已经把SSD装在硬盘位,加上对这块Optibay硬盘改装盒并不太信任的缘故。
(2) 你可能会发现大量的灰,建议好好清理干净。不建议用卫生纸直接擦,导致大量静电产生。最好用吹的。
(3) 为了顺利拆下光驱,首先你要把盖在光驱上的那个条线连接在主板的一边向上拉起,并把右下黑色的无线模块两颗螺丝松开,向下掀起。不需要完全拆下。
(4) 光驱一共由3个螺丝固定,左,右上,右下。

3. 转移光驱的固定架:

(1) 所谓光驱固定架,就是上面光驱边上突出来的那块金属,由两颗螺丝固定在光驱上。这里要拆下,转移到Optibay硬盘改装盒的相同位置上。
(2) 建议购买Macbook专用的Optibay硬盘改装盒,不然有可能改装盒上没有这两个开孔。

4. 把Optibay硬盘改装盒装到光驱位置

(1) 顺序和拆的顺序相反,很简单了。

5. 盖上后盖完成

(1) 这里我没急着把螺丝拧回去。但还是盖上后盖再开机,这样可以不破坏电磁屏蔽。
(2) 开机按Option有五个分区,我们成功了。

6. 外置光驱

(1) 我买的是一个套装,带一个USB 2.0的外置光驱盒,可以把拆出来的光驱装进去。

全部完成之后,下图是SSD的固态盘速度测试:

下图是装在OptiBay上的HDD硬盘速度测试:

下图是接在外置盒里的光驱:

找了张Mac安装盘,这是张D9双层盘,顺利读取:

 

如何使Mac OS X Lion 10.7.1支持SSD的TRIM技术

26 Aug

在默认情况下,10.6.4以后的Mac OS X只对苹果自家或者预置在Air等产品里的SSD硬盘开启TRIM技术。
TRIM技术的基本原理是,当有文件被删除时,操作系统会发给SSD一个指令,使得SSD在空闲的时候清除被删除文件所占用的存储单元。如果TRIM没有开启,那么SSD会在存储单元再次被使用时,先清除内容,再执行写入。这个区别在于传统硬盘和SSD硬盘的实现方式上。对于传统硬盘来说,写一个数据块的时候,数据块是否有内容是无所谓的,既不会减慢写入速度也不会影响其寿命,而SSD则不然,从底层实现来看,写一个新块,比写一个包含内容的块要慢,而且长期会导致SSD存储单元寿命下降。因此在SSD硬盘上使用TRIM,会提升其写操作速度,及延长SSD硬盘寿命。

具体的开启方法可见这篇博客
我翻译之后把步骤写在这里,如何打开SSD的TRIM支持:

1. 备份原来的IOAHCIBlockStorage:

打开终端(Terminal),输入以下指令(可能需要输入密码):

sudo cp /System/Library/Extensions/IOAHCIFamily.kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage /IOAHCIBlockStorage.original

这一步把系统原来的IOAHCIBlockStorage备份到了磁盘根目录/处。

2. 开启TRIM

sudo perl -pi -e ‘s|(\x52\x6F\x74\x61\x74\x69\x6F\x6E\x61\x6C\x00).{9}(\x00\x51)|$1\x00\x00\x00\x00\x00\x00\x00\x00\x00$2|sg’ /System/Library/Extensions/IOAHCIFamily.kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage

这一步更改了IOAHCIBlockStorage内容,打开TRIM。

3. 重启系统

如果一切正常,重启之后TRIM就已经被打开。

以下是恢复步骤:
如果你想关闭TRIM,执行以下命令:

sudo perl -pi -e ‘s|(\x52\x6F\x74\x61\x74\x69\x6F\x6E\x61\x6C\x00).{9}(\x00\x51)|$1\x41\x50\x50\x4C\x45\x20\x53\x53\x44$2|sg’ /System/Library/Extensions/IOAHCIFamily.kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage

实在不行,你可以恢复到之前备份的IOAHCIBlockStorage:

sudo cp /IOAHCIBlockStorage.original /System/Library/Extensions/IOAHCIFamily.kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage

开启TRIM之后,系统信息显示为:

注意下面的“TRIM Support”已经显示为“YES”,说明TRIM已经成功开启了。

机器的其他信息如下:

开启前后速度对比,虽然不明显…

开启前:

开启后:

 

Google实时搜索

08 Dec

google-realtime

早就听说Google要整合实时信息比如Twitter到搜索结果中,这次真的见识到了,而且信息是不断更新到搜索结果页面中的…

 

如何关联Silverlight和ASP.NET项目

07 Nov

silverlight-host

用过Silverlight的兄弟都清楚,当在Visual Studio 2008中建立一个Silverlight Application Project时,首先会弹出一个提示框如上图,询问是否自动生成一个ASP.NET网站来托管这个Silverlight应用。默认会建立。一般情况下我们都会选择不建立,这样会在解决方案里少生成一个项目。
自动生成一个ASP.NET网站来托管这个Silverlight应用的好处是,在每次Debug整个解决方案的时候,Visual Studio 2008会自动首先编译Silverlight项目,然后把编译结果,一个xap文件,自动拷贝到ASP.NET网站项目的ClientBin下,然后启动ASP.NET网站项目的默认页面,显示了最新版本的Silverlight应用。如下图所示。

silverlight-host2

问题是,如果你一开始没有让Visual Studio 2008建立那个网站项目,如果哪天你需要一个网站了,可能是各种原因,比如需要一个本地的Web Service等等,自己新建一个ASP.NET网站,就不能实现上面的自动拷贝xap文件了。

解决方法比较绕。这是一个MSBuild问题,你需要Unload各个项目,然后用写字板打开ASP.NET的项目文件(csproj文件),然后在PropertyGroup中加一个SilverlightApplicationList项,如图:

silverlight-host3

注意相对路径,项目名称以及项目的GUID。这些东西都可以在解决方案文件(sln文件)里找到。就不详细解释了。

silverlight-host4

Reload项目之后Build,就会发现你的项目可以自动把xap文件拷贝到ClientBin下了。

后记:作为一个使用Silverlight开发了无数年的工程师,这似乎是我第一次写关于Silverlight的博文。现在想想,大概是公司政策使然?我应该是个很守规矩的人。

 

Google Wave on my iPhone

23 Oct

wave-on-iphone

Google Wave是不是一种迅速花掉全部流量的方式呢,呵呵。
速度是真的慢啊,输入速度更是不敢恭维,这种速度在with:public灌水是不太现实了,协同工作应该还行。
看好Google Wave加入Google Apps大家庭!!

 

Google Wave and Reader 2.0

23 Oct

with:public真的是个好地方,什么是真正的地球村呢?我想这就是了。
Google Wave中的with:public是一个公共wave的大集合,真的是大杂烩的大杂烩,什么都有…而且中文的内容很多很多,当然比较低俗,贴美女的,啥都有,不禁令我为wave开放后在中国的未来捏把汗。对了,提到with:public,Twitter的public timeline也不错,不过明显不如Google Wave那么2.0。
以前总是习惯于在Google Reader中定向地获取新信息,就好像填鸭一样,人家写什么,喂给你你就接着就行了,不用挪窝,打开Google Reader就行。现在在Wave上,信息渠道更加广泛了,说白了就是一片大森林,你自己出去觅食吧…

大家都知道,野生的鸭子更好吃一些。

yige-google-wave

上图,今天在Google Wave上发现的好东东,在线听歌服务——亦歌,音质不错哦,在公司不能乱装东西同学可以试用一下。