#freeze
[[FrontPage]]

#contents

2011/08/02からのアクセス回数 &counter;

** QUnitのインストール [#o56a7494]
ひげろぐサイトの[[Titaniumでユニットテスト>http://higelog.brassworks.jp/?p=692]]
を参考にデータベース関連の単体テストを試してみます。

[[lukaso/qunit – GitHub>https://github.com/lukaso/qunit]]
からダウンロードし、以下のファイルをプロジェクトにコピーします。

- runner.js
- qunit以下すべて
- test以下すべて

app.jsの終わりに以下の行を追加します。
#pre{{
// Qunit
Titanium.include('qunit/titanium_adaptor.js');
}}

** データベースの単体テスト [#h3ed15ea]
*** record_db.jsへの追加 [#j2735ada]
データベースの単体テストを実施するために、record_db.jsに以下の関数を追加しました。

- findById: idで指定されたレコードを取得する
- deleteAll: すべてのレコードを削除する
- restore: 引数(findAllの戻り値)で与えられたレコードにテーブルを戻す

#pre{{
	this.findById = function(id) {
		this.open();
		var rows = this.db.execute( 'SELECT * FROM records WHERE id=?', id );
		var res = this.setRows(rows);
		rows.close();
		this.close();
		if (res.length > 0)
			return res[0];
		else
			return null;
	};
	this.deleteAll = function() {
		Ti.API.debug("deleteAll");
		this.open();
		var rows = this.db.execute( 'DELETE FROM records' );
		this.close();
		return true;
	};
	this.restore = function(records) {
		Ti.API.debug("restore");
		this.deleteAll();
		var last = records.length-1;
		for (i = last; i >= 0; i--)
			this.insert(records[i]);
	}
}}

*** データベース単体テストの追加 [#g394fdc4]
準備ができたので、データベース単体テスト(test/record_db_test.js)を追加します。

データを1個挿入するテストをします。
- 最初にsavedにデータベースを保存し、最後にリストア-処理を書きます。
- test関数で、単体テストを記述します。QUnit.equivを使うと複雑なデータ構造の比較が簡単に行えます。

record_db_test.js
#pre{{
Ti.include('../record_db.js');
var db = new RecordDB();
var saved = db.findAll();

test("insert one", function() {
	expect(2);
	var expected = { "id": 1, "weight": "60", "at": "Sun Jul 31 2011" };
	db.deleteAll();
	db.insert(expected);
	var one = db.findById(1);
	notEqual(null, one, "not null");
	equal(QUnit.equiv(one, expected), true, "check one");
});


db.restore(saved);
Ti.API.debug(QUnit.jsDump.parse(saved));
db = null;
}}

次に、test/tests_to_run.jsでrecord_db_test.jsをインクルードします。
#pre{{
// Tests to run
//Titanium.include('same.js');
//Titanium.include('test.js');
Titanium.include('record_db_test.js');
}}

*** 単体テストの実行 [#f18a4417]
アプリケーションを動かして実際に単体テストを実行します。

ソースの修正箇所は、単体テストの結果は、logに出力されます。Debug用のログを除くと、
次のようになります。

#pre{{
[INFO] >> >> >>TEST START: insert one
[INFO] <span class="test-message">not null</span>, expected: <span class="test-expected">
{ "id": 1, "weight": "60", "at": "Sun Jul 31 2011" }</span> result: 
<span class="test-actual">null</span>, diff: <del>{ </del><del>"id": </del>
<del>1, </del><del>"weight": </del><del>"60", </del><del>"at": </del>
<del>"Sun </del><del>Jul </del><del>31 </del><del>2011" </del><del>} </del>
<ins>null </ins>
[INFO] <span class="test-message">check one</span>, expected: <span class="test-expected">true</span>
[INFO] << << <<TEST DONE : insert one FAILURES: 0 out of TOTAL: 2
[INFO] DONE : FAILURES: 0 out of TOTAL: 2
}}
ちょっと読みづらいですが、DONE : FAILURES: 0 out of TOTAL: 2でエラー無く終了したことが確認できます。

*** 他のテストも追加 [#k857c2e5]
上手くQUnitでテストできることを確認したので、insert two, update two, delete twoのテストを追加います。
JSON形式でテストデータや予想データを記述できるので、データベースのような複雑なテストも容易に記述することができます。

#pre{{
test("insert two", function() {
	expect(1);
	var expectedAll = [{ "id": 2, "weight": "62", "at": "Tue Aug 02 2011" },
			{ "id": 1, "weight": "60", "at": "Sun Jul 31 2011" }];
	var expectedTwo = { "id": 2, "weight": "62", "at": "Tue Aug 02 2011" };
	db.insert(expectedTwo);
	var all = db.findAll();
	equal(QUnit.equiv(all, expectedAll), true, "check using equal")
});

test("update two", function() {
	expect(1);
	var updatedTwo = { "id": 2, "weight": "65", "at": "Tue Aug 02 2011" };
	db.update(updatedTwo);
	var two = db.findById(2);
	equal(QUnit.equiv(two, updatedTwo), true, "check two");
});

test("delete two", function() {
	expect(1);
	var expectedAll = [{ "id": 1, "weight": "60", "at": "Sun Jul 31 2011" }];
	var two = { "id": 2, "weight": "65", "at": "Tue Aug 02 2011" };
	db.deleteOne(two);
	var all = db.findAll();
	equal(QUnit.equiv(all, expectedAll), true, "check all");	
});
}}

*** エラーの場合 [#qb6fda1b]
単体テストでエラーが発生した場合には、ERRORログに出力されるため、コンソースに赤く表示されます。

&ref(error.png);

** お詫び [#ic9a2788]
QUnitの調査を進めた結果、データベースの時刻が20秒ほど前後する現象の原因が、
QUnitの調査を進めた結果、データベースの時刻が20秒ほど前後する障害の原因が、
Ti.App.fireEventで送った時にDateの時刻がずれることが分かりました。
また、QUnit.equalsでDate型が正しく処理できないこともあり、recordのatの型
Date型からString型に変更することにしました。

ソースの修正箇所は、

record_db_test.js
#pre{{
	this.setRows = function (rows) {
		var res = [];
		if ( rows.getRowCount() > 0 ) {
			Ti.API.debug('Found: ' + rows.getRowCount() );
			for (i =0; rows.isValidRow(); i++) {
				var record = {};
				record.id = rows.fieldByName('id');
				record.weight = rows.fieldByName('weight');
				var time = rows.fieldByName('at', Titanium.Database.FIELD_TYPE_DOUBLE)
				var at = new Date().setTime(time);
				var at = new Date();
				at.setTime(time);
				record.at = at.toDateString();
				res.push(record);
				rows.next();
			}
		}
		return res;
	};
	this.update = function(record) {
		this.open();
		var at = new Date(record.at);
		var res = this.db.execute(
		'UPDATE records SET weight=?, at=? WHERE id=?',
		record.weight,
		at.getTime(),
		record.id
		);
		Ti.API.debug('Update DB');
		this.close();
		return true;
	};
	this.insert = function(record) {
		this.open();
		var at = new Date(record.at);
		var res = this.db.execute(
		'INSERT INTO records (weight, at) VALUES(?,?)',
		record.weight,
		at.getTime()
		);
		Ti.API.debug('Insert into DB');
		this.close();
	};
}}

table_view.js
#pre{{
function updateRecord (records) {
のdateLabelの設定部分
		var dateLabel = Ti.UI.createLabel({
			width: 290,
			height: 'auto',
			left: 5,
			top: 5,
			fontSize: 6,
			textAlign: 'right'
		}
		);
		dateLabel.text = record.at;
		row.add(dateLabel);
追加ボタンのコールバック
addButton.addEventListener(
'click', function () {
	var at = new Date();
	var recordWindow = Ti.UI.createWindow({
		url: 'record_window.js',
		record: {weight:'', at: at.toDateString()},
		func: 'insert_row',
		backgroundColor:'#fff'
	});
	Ti.UI.currentTab.open(recordWindow);
});
}}

record_window.js
#pre{{
var dateField = Ti.UI.createTextField({
	value: win.record.at,
	hintText: '日付を入力してください',
	top:20, left:50, right:50, height:40,
	borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED
});
win.add(dateField);

saveButton.addEventListener(
'click', function () {
	var record = {};
	record.id = win.record.id;
	record.weight = weightField.value;
	record.at = new Date(dateField.value).toDateString();
	var at = new Date(dateField.value)
	record.at = at.toDateString();
	Ti.App.fireEvent(win.func, record);
	win.close();
});
}}

** [#o56a7494]
** プログラムソース [#n5955ebc]
ここまでのプログラムソースは、

- &ref(Demo.zip);

にまとめてあります。

** コメント [#uda5179f]
#vote(おもしろかった,そうでもない,わかりずらい)
#vote(おもしろかった[1],そうでもない[0],わかりずらい[1])

皆様のご意見、ご希望をお待ちしております。
#comment_kcaptcha


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc