2011/08/02からのアクセス回数 7110
ひげろぐサイトのTitaniumでユニットテスト を参考にデータベース関連の単体テストを試してみます。
lukaso/qunit – GitHub からダウンロードし、以下のファイルをプロジェクトにコピーします。
app.jsの終わりに以下の行を追加します。
// Qunit
Titanium.include('qunit/titanium_adaptor.js');
データベースの単体テストを実施するために、record_db.jsに以下の関数を追加しました。
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]);
}
準備ができたので、データベース単体テスト(test/record_db_test.js)を追加します。
データを1個挿入するテストをします。
record_db_test.js
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をインクルードします。
// Tests to run
//Titanium.include('same.js');
//Titanium.include('test.js');
Titanium.include('record_db_test.js');
アプリケーションを動かして実際に単体テストを実行します。
ソースの修正箇所は、単体テストの結果は、logに出力されます。Debug用のログを除くと、 次のようになります。
[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でエラー無く終了したことが確認できます。
上手くQUnitでテストできることを確認したので、insert two, update two, delete twoのテストを追加います。 JSON形式でテストデータや予想データを記述できるので、データベースのような複雑なテストも容易に記述することができます。
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");
});
単体テストでエラーが発生した場合には、ERRORログに出力されるため、コンソースに赤く表示されます。
QUnitの調査を進めた結果、データベースの時刻が20秒ほど前後する障害の原因が、 Ti.App.fireEventで送った時にDateの時刻がずれることが分かりました。 また、QUnit.equalsでDate型が正しく処理できないこともあり、recordのatの型 Date型からString型に変更することにしました。
record_db_test.js
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();
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
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
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;
var at = new Date(dateField.value)
record.at = at.toDateString();
Ti.App.fireEvent(win.func, record);
win.close();
});
ここまでのプログラムソースは、
にまとめてあります。
皆様のご意見、ご希望をお待ちしております。