单元测试

更改或添加代码后,您应该运行现有单元测试并考虑 写出更多。所有测试均在未压缩的代码版本上执行。

单元测试分为两组:JS 测试和块生成器测试。

JS 测试

JS 测试会确认 Blockly 的 核心。我们使用 Mocha 运行单元测试,使用 Sinon 创建桩依赖项,并使用 Chai 对代码进行断言。

运行测试

在 blockly 和 blockly-samples 中,npm run test 都会运行单元测试。在 这也会运行其他测试,例如执行 lint 请求和编译。您可以 还在浏览器中打开 tests/mocha/index.html,以交互方式运行所有 Mocha 测试。

编写测试

我们使用 Mocha TDD 接口运行测试。测试会整理到套件中,套件可以包含其他子套件和/或测试。通常,Blockly 的每个组件(例如 toolboxworkspace)都有自己的测试文件,其中包含一个或多个套件。每个套件都可以有一个setupteardown 分别在上述每个测试之前和之后调用的方法, 套件。

测试帮助程序

我们提供了一些专门针对 Blockly 的辅助函数,在编写测试时可能会派上用场。这些文件可在 coreblockly-samples 中找到。

辅助函数包括 sharedTestSetupsharedTestTeardown必须在测试之前和之后调用(请参阅“要求”部分) 部分)。

sharedTestSetup
  • 设置 sinon 虚构计时器(在某些测试中,您需要使用 this.clock.runAll)。
  • 将 Blockly.Events.fire 桩设置为立即触发(可配置)。
  • 设置自动清理通过 defineBlocksWithJsonArray
  • this 上下文中声明了一些 可访问:
    • this.clock(但不应恢复,否则会导致 sharedTestTeardown)
    • this.eventsFireStub
    • this.sharedCleanup(与 addMessageToCleanupaddBlockTypeToCleanup)(注意:您不需要使用 addBlockTypeToCleanup(如果您使用 defineBlocksWithJsonArray)

该函数有一个用于配置设置的可选 options 参数。目前, 仅用于确定是否对 Blockly.Events.fire 进行存根以触发 (默认情况下将存根)。

sharedTestTeardown
  • 丢弃工作区 this.workspace(具体取决于其定义位置, 请参阅“测试要求”部分了解详情)。
  • 恢复所有存根。
  • 清理通过 defineBlocksWithJsonArrayaddBlockTypeToCleanup 添加的所有块类型。
  • 清理通过 addMessageToCleanup 添加的所有消息。

测试要求

  • 每个测试都必须在最外层套件的设置的第一行调用 sharedTestSetup.call(this);,并在最外层套件的拆解最后一行调用 sharedTestTeardown.call(this);
  • 如果您需要带有通用工具箱的工作区,可以使用 预设工具箱 针对测试 index.html。有关示例,请参阅下文。
  • 您必须正确处置 this.workspace。在大多数测试中,您将在最外层套件中定义 this.workspace,并将其用于所有后续测试,但在某些情况下,您可以在内部套件中定义或重新定义它(例如,您的某个测试需要一个工作区,其选项与您最初设置的选项不同)。必须在测试结束后将其处置。
    • 如果您在最外层套件中定义了 this.workspace 且从未重新定义它,则无需执行任何进一步操作。sharedTestTeardown 会自动处理该文件。
    • 如果您是在内部套件中首次定义 this.workspace(即未在最外层套件中定义),则必须在该套件的拆解过程中调用 workspaceTeardown.call(this, this.workspace) 以手动处理它。
    • 如果您在最外层的套件中定义了 this.workpace,但随后重新定义 必须先调用 workspaceTeardown.call(this, this.workspace) 重新定义它之前 删除顶级套件中定义的原始工作区。您 还必须通过调用 workspaceTeardown.call(this, this.workspace) 内部套件

测试结构

单元测试通常遵循固定结构,可总结为 排列、执行、断言

  1. 整理:为被测行为设置世界状态和所有必要条件。
  2. Act:调用被测代码以触发被测行为。
  3. Assert:就返回值或与 用于验证对象是否正确。

在简单的测试中,可能没有任何行为需要安排,并且可以通过在断言中内嵌对被测代码的调用来组合执行和断言阶段。对于更复杂的用例,如果您坚持使用 到这 3 个阶段

下面是一个示例测试文件(已从真实文件中简化)。

suite('Flyout', function() {
  setup(function() {
    sharedTestSetup.call(this);
    this.toolboxXml = document.getElementById('toolbox-simple');
    this.workspace = Blockly.inject('blocklyDiv',
        {
          toolbox: this.toolboxXml
        });
  });

  teardown(function() {
    sharedTestTeardown.call(this);
  });

  suite('simple flyout', function() {
    setup(function() {
      this.flyout = this.workspace.getFlyout();
    });
    test('y is always 0', function() {
      // Act and assert stages combined for simple test case
      chai.assert.equal(this.flyout.getY(), 0, 'y coordinate in vertical flyout is 0');
    });
    test('x is right of workspace if flyout at right', function() {
      // Arrange
      sinon.stub(this.flyout.targetWorkspace, 'getMetrics').returns({
        viewWidth: 100,
      });
      this.flyout.targetWorkspace.toolboxPosition = Blockly.TOOLBOX_AT_RIGHT;
      this.flyout.toolboxPosition_ = Blockly.TOOLBOX_AT_RIGHT;

      // Act
      var x = this.flyout.getX();

      // Assert
      chai.assert.equal(x, 100, 'x is right of workspace');
    });
  });
});

本示例的注意事项:

  • 套件可以包含具有额外 setupteardown 的其他套件 方法。
  • 每个套件和测试都有一个描述性名称。
  • Chai 断言用于对代码进行断言。
    • 您可以提供可选的字符串参数,当 测试失败。这样可以更轻松地调试失败的测试。
    • 参数的顺序为 chai.assert.equal(actualValue, expectedValue, optionalMessage)。如果您将 actualexpected 调换,则 则这些错误消息将无意义
  • 当您不想调用实际代码时,可以使用 Sinon 为方法打桩。在 我们不想调用实际指标函数 与此测试无关。我们只关心搜索结果 被测方法。Sinon 对 getMetrics 函数进行存根以返回 预制响应,我们可以在测试断言中轻松检查。
  • 每个套件的 setup 方法应仅包含适用于所有测试的通用设置。如果针对特定行为的测试依赖于特定条件,则应在相关测试中明确说明该条件。

调试测试

  • 你可以在浏览器中打开这些测试,并使用开发者工具 并调查测试是否意外失败(或 意外通过!)。
  • 在测试套件或套件上设置 .only().skip(),以仅运行这组测试。 或跳过测试。例如:

    suite.only('Workspace', function () {
      suite('updateToolbox', function () {
        test('test name', function () {
          // ...
        });
        test.skip('test I don’t care about', function () {
          // ...
        });
      });
    });
    

    请务必在提交代码之前移除这些依赖项。

块生成器测试

每个分块都有自己的单元测试。这些测试用于验证块是否按预期生成代码,而不是函数。

  1. 在 Firefox 或 Safari 中加载 tests/generators/index.html。请注意,Chrome 和 Opera 具有安全限制,这些限制阻止从本地“file://”加载测试 系统(问题 41024) 和 47416)。
  2. 从下拉菜单中选择要测试的系统的相关部分,并且 点击“加载”屏蔽设置应显示在工作区中。
  3. 点击“JavaScript”。
    在 JavaScript 控制台中复制并运行生成的代码。如果输出以“OK”结尾,则表示测试已通过。
  4. 点击“Python”。
    Python 解释器中复制并运行生成的代码。如果输出以“OK”结尾,则表示测试已通过。
  5. 点击“PHP”。
    PHP 解释器中复制并运行生成的代码。 如果输出以“OK”结尾,则表示测试已通过。
  6. 点击“Lua”。
    复制生成的代码并在 Lua 解释器中运行。 如果输出以“OK”结尾,则表示测试已通过。
  7. 点击“Dart”。
    Dart 解释器中复制并运行生成的代码。如果输出以“OK”结尾,则表示测试已通过。

修改分块生成器测试

  1. 在浏览器中加载 tests/generators/index.html
  2. 从下拉菜单中选择系统的相关部分,然后点击 “加载”。屏蔽设置应显示在工作区中。
  3. 对这些代码块进行任何更改或添加。
  4. 点击“XML”。
  5. 将生成的 XML 复制到 tests/generators/ 中的相应文件中。