如何使用 alloy 为钛编写测试用例
How to write test cases for titanium with alloy
我在为 alloy 框架编写测试用例时卡住了,因为我不知道如何在 mocha 测试框架中使用控制器和 alloy 文件。我在 google 上进行了搜索,在代码下方建议了几个链接来模拟控制器,但它抛出了 "TypeError: alloy.createController is not a function".
的错误
var alloy = require('../../alloy');
它('Verify row controller',函数(){
console.log(JSON.stringify(alloy))
var controller = alloy.createController('login', {
name : "uniqueName",
});
// if(controller.passwordTest.value !== "uniqueName"){
// throw new ("Verify row controller FAILED");
// }
});
目前,我可以向您展示我们代码库的一个(稍作修改的)示例。
首先,我们的控制器测试是纯粹的 javascript 测试。所有对 Ti api 的调用都是针对模拟执行的。我们只关注被测控制器,所有依赖项都被模拟。
为此我们依赖 jasmine 和 jasmine-npm。
- 安装 jasmine-npm;见 https://github.com/jasmine/jasmine-npm
- 在项目的根目录中创建 'spec' 文件夹(带有 tiapp.xml 的文件夹)
- 将所有测试文件放入此文件夹
- 测试文件的文件名必须以_spec.js
结尾
运行 项目根文件夹中的 jasmine 命令
描述('authenticate controller test',函数(){
var USER_NAME = "John Doe";
var fooControllerMock = {
getView: function(){}
};
var fooViewMock = {
open: function(){}
}
Ti = {
// create here a mock for all Ti* functions and properties you invoke in your controller
}
Alloy = {
CFG: {
timeout: 100
},
Globals: {
loading: {
hide: function(){}
},
networkClient: {
hasAutoLogin: function(){}
},
notifications: {
showError: function(){}
}
},
createController: function(){}
};
var controllerUnderTest; // class under test
$ = {
btnAuthenticate: {
addEventListener: function(){}
},
authenticate: {
addEventListener: function(){},
close: function(){}
},
username: {
addEventListener: function(){},
getValue: function(){}
},
password: {
addEventListener: function(){}
},
windowContainer: {
addEventListener: function(){}
}
};
L = function(s){
return s;
};
beforeEach(function () {
controllerUnderTest = require('../app/controllers/auth');
});
it('should create foo controller when authentication was succesful', function(){
spyOn(Alloy.Globals.loading, 'hide');
spyOn(Alloy, 'createController').and.returnValue(fooControllerMock);
spyOn(fooControllerMock, 'getView').and.returnValue(fooViewMock);
spyOn($.username, 'getValue').and.returnValue(USER_NAME);
spyOn($.auth, 'close');
controllerUnderTest.test._onAuthSuccess();
expect(Alloy.Globals.loading.hide).toHaveBeenCalled();
expect(Alloy.createController).toHaveBeenCalledWith('foo');
expect(fooControllerMock.getView).toHaveBeenCalled();
expect($.auth.close).toHaveBeenCalled();
});
it('should show error message when a login error has occured', function(){
spyOn(Alloy.Globals.loading, 'hide');
spyOn(Alloy.Globals.notifications, 'showError');
controllerUnderTest.test._onAuthLoginError();
expect(Alloy.Globals.loading.hide).toHaveBeenCalled();
expect(Alloy.Globals.notifications.showError).toHaveBeenCalledWith('msg.auth.failure');
});
});
请务必为您从控制器中调用的所有 Ti* 内容编写一个模拟实现(只是空的)。我知道这很麻烦,但解决方案正在路上。
请注意,我们已经创建了一个 npm 包,其中包含为此生成的模拟(基于 api.jsca),以及一些代码,您可以使用这些代码模拟所有必需的依赖项,以及一组最佳测试做法。但是,我们将在内部验证此代码,然后再将其开源。我希望我们可以在几周内写下我们的博客 post 并公开我们随附的 github 存储库。关注tiSlack即可。
控制器代码:
function _onAuthSuccess() {
Alloy.Globals.loading.hide();
Alloy.createController('foo').getView().open();
$.authenticate.close();
}
function _onAuthLoginError() {
Alloy.Globals.loading.hide();
Alloy.Globals.notifications.showError(L('msg.auth.failure'));
}
function _onAuthTokenValidationFailure() {
Alloy.Globals.loading.hide();
}
function _authenticate() {
var username = $.username.value;
var password = $.password.value;
if(Alloy.Globals.validationEmail.isValidEmailAddress(username)){
Alloy.Globals.loading.show(L('authenticate.msg.logging.in'), false);
} else {
Alloy.Globals.notifications.showError(L('app.error.invalid.email'));
}
}
function _onNetworkAbsent() {
Alloy.Globals.loading.hide();
Alloy.Globals.notifications.showError(L('global.no.network.connection.available'));
}
function _hideKeyboard() {
$.username.blur();
$.password.blur();
}
function _focusPassword() {
$.username.blur();
$.password.focus();
}
function _init() {
Ti.App.addEventListener('auth:success', _onAuthSuccess);
Ti.App.addEventListener('auth:loginFailed', _onAuthLoginError);
Ti.App.addEventListener('app:parseError', _onAppParseError);
Ti.App.addEventListener('network:none', _onNetworkAbsent);
$.btnAuthenticate.addEventListener('click', ..);
$.authenticate.addEventListener('close', _cleanup);
$.username.addEventListener('return', _focusPassword);
$.password.addEventListener('return', _authenticate);
$.windowContainer.addEventListener('touchstart', _hideKeyboard);
}
_init();
function _cleanup() {
Ti.API.info('Closing and destroying the auth controller');
...
$.windowContainer.removeEventListener('touchstart', _hideKeyboard);
$.destroy();
$.off();
}
module.exports = {
test: {
_onAuthSuccess: _onAuthSuccess,
_onAuthLoginError: _onAuthLoginError
}
}
和相应的视图:
<Alloy>
<Window>
<View id="windowContainer">
<TextField id="username" />
<TextField id="password" >
<Button id="btnAuthenticate" />
</View>
</Window>
</Alloy>
Titanium 内部使用 Ti-Mocha to write unit-tests for all of it's components. It's a modified version of Mocha 并使用测试套件、测试用例、链接等来测试合适的代码覆盖率。试试吧!
我在为 alloy 框架编写测试用例时卡住了,因为我不知道如何在 mocha 测试框架中使用控制器和 alloy 文件。我在 google 上进行了搜索,在代码下方建议了几个链接来模拟控制器,但它抛出了 "TypeError: alloy.createController is not a function".
的错误var alloy = require('../../alloy'); 它('Verify row controller',函数(){
console.log(JSON.stringify(alloy))
var controller = alloy.createController('login', {
name : "uniqueName",
});
// if(controller.passwordTest.value !== "uniqueName"){
// throw new ("Verify row controller FAILED");
// }
});
目前,我可以向您展示我们代码库的一个(稍作修改的)示例。
首先,我们的控制器测试是纯粹的 javascript 测试。所有对 Ti api 的调用都是针对模拟执行的。我们只关注被测控制器,所有依赖项都被模拟。
为此我们依赖 jasmine 和 jasmine-npm。
- 安装 jasmine-npm;见 https://github.com/jasmine/jasmine-npm
- 在项目的根目录中创建 'spec' 文件夹(带有 tiapp.xml 的文件夹)
- 将所有测试文件放入此文件夹
- 测试文件的文件名必须以_spec.js 结尾
运行 项目根文件夹中的 jasmine 命令
描述('authenticate controller test',函数(){
var USER_NAME = "John Doe"; var fooControllerMock = { getView: function(){} }; var fooViewMock = { open: function(){} } Ti = { // create here a mock for all Ti* functions and properties you invoke in your controller } Alloy = { CFG: { timeout: 100 }, Globals: { loading: { hide: function(){} }, networkClient: { hasAutoLogin: function(){} }, notifications: { showError: function(){} } }, createController: function(){} }; var controllerUnderTest; // class under test $ = { btnAuthenticate: { addEventListener: function(){} }, authenticate: { addEventListener: function(){}, close: function(){} }, username: { addEventListener: function(){}, getValue: function(){} }, password: { addEventListener: function(){} }, windowContainer: { addEventListener: function(){} } }; L = function(s){ return s; }; beforeEach(function () { controllerUnderTest = require('../app/controllers/auth'); }); it('should create foo controller when authentication was succesful', function(){ spyOn(Alloy.Globals.loading, 'hide'); spyOn(Alloy, 'createController').and.returnValue(fooControllerMock); spyOn(fooControllerMock, 'getView').and.returnValue(fooViewMock); spyOn($.username, 'getValue').and.returnValue(USER_NAME); spyOn($.auth, 'close'); controllerUnderTest.test._onAuthSuccess(); expect(Alloy.Globals.loading.hide).toHaveBeenCalled(); expect(Alloy.createController).toHaveBeenCalledWith('foo'); expect(fooControllerMock.getView).toHaveBeenCalled(); expect($.auth.close).toHaveBeenCalled(); }); it('should show error message when a login error has occured', function(){ spyOn(Alloy.Globals.loading, 'hide'); spyOn(Alloy.Globals.notifications, 'showError'); controllerUnderTest.test._onAuthLoginError(); expect(Alloy.Globals.loading.hide).toHaveBeenCalled(); expect(Alloy.Globals.notifications.showError).toHaveBeenCalledWith('msg.auth.failure'); }); });
请务必为您从控制器中调用的所有 Ti* 内容编写一个模拟实现(只是空的)。我知道这很麻烦,但解决方案正在路上。
请注意,我们已经创建了一个 npm 包,其中包含为此生成的模拟(基于 api.jsca),以及一些代码,您可以使用这些代码模拟所有必需的依赖项,以及一组最佳测试做法。但是,我们将在内部验证此代码,然后再将其开源。我希望我们可以在几周内写下我们的博客 post 并公开我们随附的 github 存储库。关注tiSlack即可。
控制器代码:
function _onAuthSuccess() {
Alloy.Globals.loading.hide();
Alloy.createController('foo').getView().open();
$.authenticate.close();
}
function _onAuthLoginError() {
Alloy.Globals.loading.hide();
Alloy.Globals.notifications.showError(L('msg.auth.failure'));
}
function _onAuthTokenValidationFailure() {
Alloy.Globals.loading.hide();
}
function _authenticate() {
var username = $.username.value;
var password = $.password.value;
if(Alloy.Globals.validationEmail.isValidEmailAddress(username)){
Alloy.Globals.loading.show(L('authenticate.msg.logging.in'), false);
} else {
Alloy.Globals.notifications.showError(L('app.error.invalid.email'));
}
}
function _onNetworkAbsent() {
Alloy.Globals.loading.hide();
Alloy.Globals.notifications.showError(L('global.no.network.connection.available'));
}
function _hideKeyboard() {
$.username.blur();
$.password.blur();
}
function _focusPassword() {
$.username.blur();
$.password.focus();
}
function _init() {
Ti.App.addEventListener('auth:success', _onAuthSuccess);
Ti.App.addEventListener('auth:loginFailed', _onAuthLoginError);
Ti.App.addEventListener('app:parseError', _onAppParseError);
Ti.App.addEventListener('network:none', _onNetworkAbsent);
$.btnAuthenticate.addEventListener('click', ..);
$.authenticate.addEventListener('close', _cleanup);
$.username.addEventListener('return', _focusPassword);
$.password.addEventListener('return', _authenticate);
$.windowContainer.addEventListener('touchstart', _hideKeyboard);
}
_init();
function _cleanup() {
Ti.API.info('Closing and destroying the auth controller');
...
$.windowContainer.removeEventListener('touchstart', _hideKeyboard);
$.destroy();
$.off();
}
module.exports = {
test: {
_onAuthSuccess: _onAuthSuccess,
_onAuthLoginError: _onAuthLoginError
}
}
和相应的视图:
<Alloy>
<Window>
<View id="windowContainer">
<TextField id="username" />
<TextField id="password" >
<Button id="btnAuthenticate" />
</View>
</Window>
</Alloy>
Titanium 内部使用 Ti-Mocha to write unit-tests for all of it's components. It's a modified version of Mocha 并使用测试套件、测试用例、链接等来测试合适的代码覆盖率。试试吧!