许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称被称为 别名
(如果类扩展了 Ext.Component,则为 xtype
)。别名/xtype 列在适用类的类名旁边,以便快速参考。
框架类或其成员可以指定为 private
或 protected
。否则,类/成员为 public
。Public
、protected
和 private
是访问描述符,用于传达类或类成员应如何以及何时使用。
Public 类和类成员可供任何其他类或应用程序代码使用,并且在主要产品版本中可以作为稳定且持久的内容来依赖。公共类和成员可以通过子类安全地扩展。
Protected 类成员是稳定的 public
成员,旨在由拥有类或其子类使用。受保护的成员可以通过子类安全地扩展。
Private 类和类成员在框架内部使用,不供应用程序开发人员使用。私有类和成员可能会随时更改或从框架中省略,恕不另行通知,并且不应在应用程序逻辑中依赖。
static
标签。*见下面的静态。下面是一个示例类成员,我们可以对其进行剖析以显示类成员的语法(在本例中为从 Ext.button.Button 类查看的 lookupComponent 方法)。
让我们看看成员行的每个部分
lookupComponent
)( item )
)Ext.Component
)。对于不返回除 undefined
以外的任何内容的方法,或者可能显示为以正斜杠 /
分隔的多个可能值,这可能会被省略,这表示返回的内容可能取决于方法调用的结果(即,如果 get 方法调用成功,方法可能会返回 Component,如果失败,则返回 false
,这将显示为 Ext.Component/Boolean
)。PROTECTED
- 请参阅下面的标志部分)Ext.container.Container
)。如果成员源自当前类,则源类将显示为蓝色链接;如果成员从祖先类或混合类继承,则显示为灰色。查看源代码
)item : Object
)。undefined
以外的值,“返回值”部分将注释返回的类或对象的类型以及描述(示例中的 Ext.Component
)Available since 3.4.0
- 未在示例中显示),就在成员描述之后Defaults to: false
)API 文档使用许多标志来进一步传达类成员的功能和意图。标签可以用文本标签、缩写或图标表示。
classInstance.method1().method2().etc();
false
,则标记为可阻止的事件将不会触发- 表示框架类
- Singleton 框架类。*有关更多信息,请参阅 singleton 标志
- 组件类型框架类(Ext JS 框架中扩展 Ext.Component 的任何类)
- 表示类、成员或指南在当前查看的版本中是新的
- 表示类型为 config
的类成员
- 表示类型为 property
的类成员
- 表示类型为 method
的类成员
- 表示类型为 event
的类成员
- 表示类型为 theme variable
的类成员
- 表示类型为 theme mixin
的类成员
- 表示类、成员或指南在当前查看的版本中是新的
在 API 文档页面上的类名正下方是一行按钮,对应于当前类拥有的成员类型。每个按钮都显示按类型划分的成员计数(此计数会随着应用过滤器而更新)。单击按钮会将您导航到该成员部分。将鼠标悬停在成员类型按钮上将显示该类型的所有成员的弹出菜单,以便快速导航。
与类配置选项相关的 Getter 和 setter 方法将在方法部分以及 API 文档和成员类型菜单的配置部分中显示,就在它们所适用的配置下方。Getter 和 setter 方法文档将在配置行中找到,以便于参考。
您的页面历史记录保存在本地存储中,并在顶部标题栏下方显示(使用可用的实际空间)。默认情况下,仅显示的搜索结果是与您当前查看的产品/版本匹配的页面。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选按钮来扩展显示的内容。这将显示所有产品/版本的所有最近页面访问记录。
在历史记录配置菜单中,您还将看到最近页面访问的列表。结果按“当前产品/版本”和“全部”单选按钮过滤。单击 按钮将清除历史记录栏以及本地存储中保存的历史记录。
如果在历史记录配置菜单中选择“全部”,“在历史记录栏中显示产品详细信息”的复选框选项将启用。选中后,每个历史页面的产品/版本将与历史记录栏中的页面名称一起显示。将光标悬停在历史记录栏中的页面名称上也会显示产品/版本作为工具提示。
可以使用页面顶部的搜索字段搜索 API 文档和指南。
在 API 文档页面上,还有一个过滤器输入字段,该字段使用过滤器字符串过滤成员行。除了按字符串过滤外,您还可以按访问级别、继承和只读过滤类成员。这是通过使用页面顶部的复选框完成的。
API 类导航树底部的复选框过滤类列表以包括或排除私有类。
单击空的搜索字段将显示您最近 10 次的搜索,以便快速导航。
每个 API 文档页面(JavaScript 原始类型页面除外)都有一个与该类相关的元数据菜单视图。此元数据视图将具有以下一项或多项
Ext.button.Button
类具有 Ext.Button
的备用类名)。备用类名通常为了向后兼容性而维护。可运行的示例 (Fiddles) 默认在页面上展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。切换所有状态将在页面加载之间记住。
类成员默认在页面上折叠。您可以使用成员行左侧的箭头图标或全局使用右上角的展开/折叠全部切换按钮来展开和折叠成员。
在较窄的屏幕或浏览器上查看文档将导致针对较小外形尺寸优化的视图。桌面视图和“移动”视图之间的主要区别在于
可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“查看源代码”链接来查看类成员的源代码。
在 Sencha Test 中编写的测试将分为以下类别
单元测试
功能测试
了解如何利用每种类型的测试将确保应用程序内最高的完整性。
单元测试可以描述为针对应用程序底层代码的测试。这种测试风格可能对应用程序开发人员特别感兴趣。
功能测试分析正在运行的应用程序,而不是简单地测试其各个部分。
这种测试风格可能对质量保证和最终用户特别感兴趣。
让我们澄清这种概括性的描述,即单元测试可以在不启动应用程序的情况下执行,而功能测试需要启动应用程序才能执行测试。
这种区别很重要,因为 Sencha Test 以及 Ext JS 5+ 可以测试代码而无需任何 UI 存在。例如,您可以测试数据模型中的验证,而无需运行应用程序。应用程序中使用的视图也可以独立于应用程序进行测试。
当通过运行的应用程序进行测试时,应用程序的许多方面可能难以测试或耗时。测试可能需要您首先遵循一组特定的导航步骤,或者以特定用户类型身份登录。
例如,考虑这样一种情况,您想测试“用户”是否可以编辑给定字段,而不是“用户详细信息”面板上的“管理员”角色。功能测试要求您启动应用程序并以用户身份而不是管理员身份登录。然后,可能导航到“用户列表”视图,从列表中选择一个用户,然后单击编辑按钮以启动“用户详细信息”弹出窗口。然后,您需要评估“用户”有资格编辑的字段。
最终,我们只想知道“用户”是否可以在“用户详细信息”类型的视图中编辑某些字段。通过单元测试,只需创建一个仅启动注入“用户”权限的视图的测试即可。然后,您可以根据权限评估字段的可编辑性。
在探索功能测试之前,让我们看看一些常见的单元测试用例,以及我们如何使用 Sencha Studio 执行这些测试。
注意: 单元测试不需要应用程序或 Sencha 框架。也就是说,我们正在遵循此模型来创建从头到尾的指南路径。
对于此单元测试,让我们测试 User
模型的验证。我们希望确保表示输入用户数据的数据存根在我们的参数范围内有效。
首先,让我们通过添加以下文件将我们的 User
模型类添加到我们的应用程序:“{appName}/app/model/User.js”
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
fields: ['first', 'last', 'fullname', 'username'],
validators: {
first: 'presence',
last: { type: 'length', min: 2 },
username: [
{ type: 'exclusion', list: ['Admin', 'Operator'] },
{ type: 'format', matcher: /([a-z]+)[0-9]{2,3}/i }
]
}
});
现在可以实例化“User”模型以用于测试目的。接下来,让我们向场景添加一些数据,以便测试在验证我们的 User 模型时能够访问它。我们将在工作区根目录中创建一个文件夹/文件:“{workspaceRoot}/shared/userdata.js”。“userdata”文件将包含以下内容
var __my_user_data = {
first: 'Rick',
last: 'Grimes',
username: 'rgrimes'
}
接下来,我们将“userdata”文件添加到测试项目中,以便任何场景的测试都可以访问它。我们将通过单击测试项目的“附加库”标题下的“+ 添加”按钮来完成此操作,双击默认字段值,单击文件夹图标以调出文件选择器,然后从“[workspaceRoot]/shared”目录中选择“userdata.js”文件。最后,单击顶部工具栏中的“保存”按钮。
在下一步中,我们将创建用于验证“userdata”值是否由我们的“User”模型类验证的单元测试。
首先,通过单击“测试项目”视图中“场景”标题下的“+ 添加”按钮来创建场景。为了本指南的目的,我们将为其命名为“User Data”。
接下来,右键单击工作区导航树中的“User Data”节点,然后选择“新建 > Jasmine 测试套件”,并将用户验证测试命名为“UserValidation”。这会将“UserValidation”测试添加为“User Data”场景的子项。
单击工作区验证树中的“UserValidation.js”节点以打开测试编辑器视图。
最后,将占位符测试脚本替换为以下内容
describe("UserValidation", function() {
it("should pass", function(done) {
Ext.require('MyApplication.model.User', function () {
var user = new MyApplication.model.User(__my_user_data),
failedValidation = user.getValidation().dirty;
expect(failedValidation).toBe(!true);
done();
});
});
});
在此测试中,我们期望模型验证示例用户数据。如果您在场景的测试运行器中播放测试,您应该会看到它确实通过了!
除了测试非视图逻辑外,我们还可以单元测试我们较大应用程序的各个部分,以确保它们正常工作。在此测试中,让我们创建一个带有徽章的按钮,该徽章显示在按钮的 badgeText
配置中设置的文本。
首先,将以下文件夹/文件添加到您的应用程序中,路径为:“{workspaceRoot}/app/view/button/BadgeButton.js”
Ext.define('MyApplication.view.button.BadgeButton', {
extend: 'Ext.button.Button',
xtype: 'badgebutton',
config: {
badgeText: null
},
childEls: ['badgeEl'],
cls: 'da-badge-button',
hideBadgeOnDisabled: true,
initRenderData: function() {
return Ext.apply(this.callParent(), {
badgeText: this.badgeText
});
},
updateBadgeText: function(text) {
var me = this;
if (me.rendered) {
me.badgeEl.setHtml(text).setVisible(text);
} else {
me.on('render', function() {
me.badgeEl.setVisibilityMode(Ext.dom.Element.DISPLAY);
me.badgeEl.setHtml(text).setVisible(text);
});
}
},
onDisable: function() {
this.callParent();
if (this.hideBadgeOnDisabled) {
this.badgeEl.hide();
}
},
onEnable: function() {
this.callParent();
this.badgeEl.show();
}
}, function(BadgeButton) {
BadgeButton.prototype.renderTpl += '<span id="{id}-badgeEl" class="da-badgeElCls" data-ref="badgeEl">{badgeText}</span>';
});
接下来,在我们的应用程序的测试项目中创建一个名为“Badge Button”的新场景,然后右键单击该场景以创建一个名为“ButtonText”的新 Jasmine 测试套件。
我们可以在徽章按钮测试套件中添加一个带有多个 it()
规范的 describe()
。每个 it()
的描述将显示为“ButtonText”下场景测试树下的节点。
在“ButtonText”选项卡中输入以下测试。这将允许 Studio 评估徽章文本,并确保在禁用按钮时隐藏徽章元素。
describe("ButtonText", function() {
var btn;
beforeEach(function(){
btn = Ext.create('MyApplication.view.button.BadgeButton', {
renderTo: Ext.getBody()
});
});
afterEach(function () {
btn.destroy();
});
it("should have a badge of 2", function() {
btn.setBadgeText('update');
expect(btn.badgeEl.getHtml()).toBe('update');
});
it("badge should be hidden", function() {
btn.setBadgeText('update');
btn.disable();
expect(btn.badgeEl.isVisible()).toBe(false);
});
});
运行测试应显示“ButtonText”中两个规范的绿色复选标记。
单元测试是敏捷的,允许对内部代码和隔离组件进行离散测试。当防止随着应用程序发展和变得更加复杂而引入的错误时,单元测试提供了很好的第一道防线。但是,功能测试在错误防御策略中也占有一席之地,不应仅仅为了单元测试而被忽视。
套件的单元测试完全有可能全部通过,让您相信您的应用程序已经过防错处理。所有这一切,只是为了发现在您运行应用程序时,应用程序环境的某些动态元素未在单元测试中捕获。
对于某些操作,完善测试设置的唯一实用方法是启动应用程序并使其经历考验。
一个这样的例子是在应用程序内的视图之间导航。
假设您的应用程序有一个带有卡片视图的父容器和两个子视图:主页和用户列表。
然后,假设您想测试当 URL 包含 #!/user/rgrimes
时路由是否有效以显示特定用户的详细信息视图。
在内部,您在父视图的 viewController 中有一个路由,该路由基于 #!/user/
路由到用户面板,并且用户面板的 viewController 使用哈希的 /rgrimes
部分打开用户详细信息面板。测试这种类型的导航逻辑正是功能测试发挥作用的地方。应用程序的启动负责呈现主容器及其子项,而主容器及其子项又启动各自的 viewController 来管理视图之间的路由逻辑。
复杂路由是您希望从 Studio 启动应用程序并运行测试逻辑以验证应用程序功能的一种情况。您可能希望采用功能测试的另一个原因是为了测试在真实世界环境中运行的应用程序。
假设您设置了一个单元测试,以确保“用户”可以查看“用户详细信息”视图,但“主管”和“管理员”可以编辑。您有一个单元测试,以确保“每周概览”在用户查看时显示单个用户的详细信息,但在主管查看时显示整个团队汇总。
只有管理员可以访问配置用户角色的“管理员仪表板”,而主管和用户则不能。有了所有这些单元测试,您可能会觉得已经涵盖了所有基础,并且应用程序的每个独特方面都经过了测试,因此您可以部署。现在想象一下,用户启动应用程序但无法登录,因为与服务器/数据库的连接已断开。从远程服务器下载用户角色信息的功能测试本可以在应用程序部署之前解决此问题。
在保护您的应用程序免受错误侵害时,单元测试和功能测试至关重要。
幸运的是,Sencha Test 为您提供了管理和执行这两者所需的工具。本指南提供了一些简单的单元测试示例,但是随着您构建测试库,您将希望合并更多内容,而不仅仅是直接的逻辑测试。您将希望模拟 UI 中的用户交互。
要了解如何轻松地将模拟用户交互添加到您的单元/功能测试中,请查看 事件记录器指南。