단위 테스트

코드를 변경하거나 추가한 후에는 기존 단위 테스트를 실행하고 코드를 더 작성하는 것이 좋습니다. 모든 테스트는 압축되지 않은 버전의 코드에서 실행됩니다.

단위 테스트에는 JS 테스트와 블록 생성기 테스트의 두 가지 세트가 있습니다.

JS 테스트

JS 테스트는 Blockly 핵심의 내부 JavaScript 함수 작동을 확인합니다. Mocha를 사용하여 단위 테스트를 실행하고, Sinon을 사용하여 종속 항목 스텁을 만들고, Chai를 사용하여 코드에 관해 어설션합니다.

테스트 실행

blockly와 blockly-samples 모두에서 npm run test가 단위 테스트를 실행합니다. 차단적으로, 린트 작업 및 컴파일과 같은 다른 테스트도 실행됩니다. 브라우저에서 tests/mocha/index.html를 열어 모든 Mocha 테스트를 대화형으로 실행할 수도 있습니다.

테스트 작성

Google은 Mocha TDD 인터페이스를 사용하여 테스트를 실행합니다. 테스트는 추가 하위 도구 모음 또는 테스트를 모두 포함할 수 있는 도구 모음으로 구성됩니다. 일반적으로 Blockly의 각 구성요소 (예: toolbox 또는 workspace)에는 하나 이상의 모음을 포함하는 자체 테스트 파일이 있습니다. 각 모음에는 해당 모음의 각 테스트 전후에 각각 호출되는 setupteardown 메서드가 있을 수 있습니다.

테스트 도우미

테스트를 작성할 때 유용할 수 있는 Blockly 관련 도우미 함수가 많이 있습니다. 이러한 함수는 coreblockly-samples에서 확인할 수 있습니다.

도우미 함수에는 테스트 전후에 호출해야 하는 sharedTestSetupsharedTestTeardown가 포함되며 (요구사항 섹션 참고),

sharedTestSetup:
  • sinon 가짜 타이머를 설정합니다 (일부 테스트에서는 this.clock.runAll를 사용해야 함).
  • 즉시 실행되도록 Blockly.Events.fire 스텁 (구성 가능)
  • defineBlocksWithJsonArray를 통해 정의된 blockTypes의 자동 정리를 설정합니다.
  • 액세스할 수 있는 this 컨텍스트의 몇 가지 속성을 선언합니다.
    • this.clock (그러나 복원하면 sharedTestTeardown에서 문제가 발생하므로 복원해서는 안 됨)
    • this.eventsFireStub
    • this.sharedCleanup (addMessageToCleanupaddBlockTypeToCleanup와 함께 사용) (참고: defineBlocksWithJsonArray를 사용하여 블록을 정의한 경우 addBlockTypeToCleanup를 사용할 필요가 없음)

이 함수에는 설정을 구성하는 선택적 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. Arrange: 테스트 중인 동작의 실제 상태와 필요한 조건을 설정합니다.
  2. Act: 테스트 대상 코드를 호출하여 테스트 중인 동작을 트리거합니다.
  3. Assert: 올바름을 확인하기 위해 반환 값 또는 모의 객체와의 상호작용에 관해 어설션합니다.

간단한 테스트에서는 정렬할 동작이 없을 수 있으며, 어설션에서 테스트 대상 코드 호출을 인라인으로 처리하여 작업 단계와 어설션 단계를 결합할 수 있습니다. 더 복잡한 경우에는 이 세 단계를 따르는 것이 테스트를 더 읽기 쉽게 만듭니다.

다음은 실제 파일을 단순화한 테스트 파일의 예입니다.

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://' 시스템에서 테스트를 로드하지 못하도록 하는 보안 제한사항이 있습니다 (문제 4102447416).
  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/의 적절한 파일에 복사합니다.