文档帮助

术语、图标和标签

许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称被称为 别名(如果类扩展了 Ext.Component,则为 xtype)。别名/xtype 列在适用类的类名旁边,以便快速参考。

访问级别

框架类或其成员可以指定为 privateprotected。否则,类/成员为 publicPublicprotectedprivate 是访问描述符,用于传达类或类成员应如何以及何时使用。

成员类型

成员语法

下面是一个示例类成员,我们可以对其进行剖析以显示类成员的语法(在本例中为从 Ext.button.Button 类查看的 lookupComponent 方法)。

让我们看看成员行的每个部分

  • 展开/折叠 - 在成员行的左侧是一个控件,用于展开和折叠每个成员行以显示/隐藏成员详细信息。
  • 成员名称 - 类成员的名称(本例中为 lookupComponent
  • 方法参数 - 方法使用的任何必需或可选参数(或传递给事件处理方法的参数)将列在方法名称旁边的括号内(本例中为 ( item )
  • 返回类型 - 方法或属性返回的类实例或 javascript 对象(本例中为 Ext.Component)。对于不返回除 undefined 以外的任何内容的方法,或者可能显示为以正斜杠 / 分隔的多个可能值,这可能会被省略,这表示返回的内容可能取决于方法调用的结果(即,如果 get 方法调用成功,方法可能会返回 Component,如果失败,则返回 false,这将显示为 Ext.Component/Boolean)。
  • 标志 - 适用于成员的任何标志将显示在旁边(本例中为 PROTECTED - 请参阅下面的标志部分)
  • 成员来源 - 在成员行的右侧是最初描述成员的类(本例中为 Ext.container.Container)。如果成员源自当前类,则源类将显示为蓝色链接;如果成员从祖先类或混合类继承,则显示为灰色。
  • 成员源代码 - 在成员来源类的右侧下方是查看成员源代码的链接(示例中的 查看源代码
  • 参数列表 - 类方法的每个参数都将使用上面括号中找到的相同名称、预期的类或对象类型以及参数的描述列出(示例中的 item : Object)。
  • 返回值 - 如果类返回除 undefined 以外的值,“返回值”部分将注释返回的类或对象的类型以及描述(示例中的 Ext.Component
  • Since (未在示例中显示) - 某些成员将显示该成员首次引入的产品版本(即 Available since 3.4.0 - 未在示例中显示),就在成员描述之后
  • Default (未在示例中显示) - Configs 通常显示要应用于类实例的默认配置值(如果未被覆盖)(即 Defaults to: false

成员标志

API 文档使用许多标志来进一步传达类成员的功能和意图。标签可以用文本标签、缩写或图标表示。

  • Required - 实例化类时所需的配置
  • Bindable - 配置具有 setter,允许通过 ViewModel 绑定设置此配置
  • Read Only - 该属性可以读取,但不能用于在运行时配置/重新配置类实例
  • Singleton - Singleton 类在定义后立即实例化,不能手动实例化
  • Static - 静态方法或属性是属于类本身的方法或属性,而不是类的实例
  • Chainable - 指的是在调用时返回类实例的方法。
    这使链式方法调用成为可能,例如:classInstance.method1().method2().etc();
  • Deprecated - 计划在未来框架版本中删除的类或成员,并在当前版本中提供以实现向后兼容性。
    已弃用的类和成员将包含一条消息,指示您未来首选的类/方法。
  • Removed - 已删除的类或成员,仅在文档中作为在框架版本之间升级的用户的参考而存在
  • Template - 在基类中定义的方法,旨在由子类覆盖
  • Abstract - 类或成员可以定义为抽象类。抽象类和成员建立类结构并提供有限的(如果有的话)代码。特定于类的代码将通过子类中的覆盖来提供。
  • Preventable - 如果从事件处理程序返回 false,则标记为可阻止的事件将不会触发

类图标

- 表示框架类

- Singleton 框架类。*有关更多信息,请参阅 singleton 标志

- 组件类型框架类(Ext JS 框架中扩展 Ext.Component 的任何类)

- 表示类、成员或指南在当前查看的版本中是新的

成员图标

- 表示类型为 config 的类成员

- 表示类型为 property 的类成员

- 表示类型为 method 的类成员

- 表示类型为 event 的类成员

- 表示类型为 theme variable 的类成员

- 表示类型为 theme mixin 的类成员

- 表示类、成员或指南在当前查看的版本中是新的

类成员快速导航菜单

在 API 文档页面上的类名正下方是一行按钮,对应于当前类拥有的成员类型。每个按钮都显示按类型划分的成员计数(此计数会随着应用过滤器而更新)。单击按钮会将您导航到该成员部分。将鼠标悬停在成员类型按钮上将显示该类型的所有成员的弹出菜单,以便快速导航。

Getter 和 Setter 方法

与类配置选项相关的 Getter 和 setter 方法将在方法部分以及 API 文档和成员类型菜单的配置部分中显示,就在它们所适用的配置下方。Getter 和 setter 方法文档将在配置行中找到,以便于参考。

历史记录栏

您的页面历史记录保存在本地存储中,并在顶部标题栏下方显示(使用可用的实际空间)。默认情况下,仅显示的搜索结果是与您当前查看的产品/版本匹配的页面。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选按钮来扩展显示的内容。这将显示所有产品/版本的所有最近页面访问记录。

在历史记录配置菜单中,您还将看到最近页面访问的列表。结果按“当前产品/版本”和“全部”单选按钮过滤。单击 按钮将清除历史记录栏以及本地存储中保存的历史记录。

如果在历史记录配置菜单中选择“全部”,“在历史记录栏中显示产品详细信息”的复选框选项将启用。选中后,每个历史页面的产品/版本将与历史记录栏中的页面名称一起显示。将光标悬停在历史记录栏中的页面名称上也会显示产品/版本作为工具提示。

搜索和过滤器

可以使用页面顶部的搜索字段搜索 API 文档和指南。

在 API 文档页面上,还有一个过滤器输入字段,该字段使用过滤器字符串过滤成员行。除了按字符串过滤外,您还可以按访问级别、继承和只读过滤类成员。这是通过使用页面顶部的复选框完成的。

API 类导航树底部的复选框过滤类列表以包括或排除私有类。

单击空的搜索字段将显示您最近 10 次的搜索,以便快速导航。

API 文档类元数据

每个 API 文档页面(JavaScript 原始类型页面除外)都有一个与该类相关的元数据菜单视图。此元数据视图将具有以下一项或多项

  • Alternate Name - 一个或多个附加的类名同义词(在 Ext JS 6.0.0 中,Ext.button.Button 类具有 Ext.Button 的备用类名)。备用类名通常为了向后兼容性而维护。
  • Hierarchy - 层次结构视图列出了当前类的继承链,一直到其祖先类,直到根基类。
  • Mixins - 混合到当前类中的类列表
  • Inherited Mixins - 混合到当前类的祖先中的类列表
  • Requires - 类实例化所需定义的所有类
  • Uses - 类在其生命周期中的某个时刻可能使用的类列表,但不一定是类最初实例化所必需的
  • Subclasses - 扩展当前类的类

展开和折叠示例及类成员

可运行的示例 (Fiddles) 默认在页面上展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。切换所有状态将在页面加载之间记住。

类成员默认在页面上折叠。您可以使用成员行左侧的箭头图标或全局使用右上角的展开/折叠全部切换按钮来展开和折叠成员。

桌面 vs 移动视图

在较窄的屏幕或浏览器上查看文档将导致针对较小外形尺寸优化的视图。桌面视图和“移动”视图之间的主要区别在于

  • 全局导航将位于左侧菜单中,可通过汉堡菜单图标访问。菜单包含以下内容(在大多数页面上)
    • 当前产品的名称(作为指向产品着陆页的链接)
    • 用于导航回文档主页的 Sencha 图标
    • 产品菜单下拉按钮
    • API 文档和指南的导航树选项卡
  • 当前上下文导航和工具位于右侧,可通过齿轮图标访问。上下文菜单包含以下内容
    • 全局搜索输入字段
    • (API 文档) “过滤器”选项卡,其中包含成员过滤器、展开/折叠所有示例按钮、展开/折叠所有成员行按钮、访问级别过滤器复选框以及每个成员的计数
    • (API 文档) “相关类”选项卡,其中包含与当前类相关的元数据菜单
    • (指南) 指南的目录

查看类源代码

可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“查看源代码”链接来查看类成员的源代码。

Sencha Test 2.4.0


顶部

单元测试 vs 功能测试

在 Sencha Test 中编写的测试将分为以下类别

  • 单元测试

  • 功能测试

了解如何利用每种类型的测试将确保应用程序内最高的完整性。

测试风格概述

单元测试可以描述为针对应用程序底层代码的测试。这种测试风格可能对应用程序开发人员特别感兴趣。

功能测试分析正在运行的应用程序,而不是简单地测试其各个部分。
这种测试风格可能对质量保证和最终用户特别感兴趣。

让我们澄清这种概括性的描述,即单元测试可以在不启动应用程序的情况下执行,而功能测试需要启动应用程序才能执行测试。

这种区别很重要,因为 Sencha Test 以及 Ext JS 5+ 可以测试代码而无需任何 UI 存在。例如,您可以测试数据模型中的验证,而无需运行应用程序。应用程序中使用的视图也可以独立于应用程序进行测试。

当通过运行的应用程序进行测试时,应用程序的许多方面可能难以测试或耗时。测试可能需要您首先遵循一组特定的导航步骤,或者以特定用户类型身份登录。

例如,考虑这样一种情况,您想测试“用户”是否可以编辑给定字段,而不是“用户详细信息”面板上的“管理员”角色。功能测试要求您启动应用程序并以用户身份而不是管理员身份登录。然后,可能导航到“用户列表”视图,从列表中选择一个用户,然后单击编辑按钮以启动“用户详细信息”弹出窗口。然后,您需要评估“用户”有资格编辑的字段。

最终,我们只想知道“用户”是否可以在“用户详细信息”类型的视图中编辑某些字段。通过单元测试,只需创建一个仅启动注入“用户”权限的视图的测试即可。然后,您可以根据权限评估字段的可编辑性。

在探索功能测试之前,让我们看看一些常见的单元测试用例,以及我们如何使用 Sencha Studio 执行这些测试。

单元测试

假设:

  • 您已按照 安装指南 进行操作
  • 您已使用经典示例应用程序创建了一个项目
  • 您已为您的应用程序初始化了测试项目 (Tests) 并创建了一个场景来保存测试。

注意: 单元测试不需要应用程序或 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 中的用户交互。

要了解如何轻松地将模拟用户交互添加到您的单元/功能测试中,请查看 事件记录器指南

Sencha Test 2.4.0