なぜテストするのか?

テストがあなたの生活にもたらす喜びを除けば、テストは私たちのアプリケーションが期待通りに動作することを保証するだけでなく、コードの使い方の簡潔な例を提供するためにも重要です。

テストは私たちのコードベースの一部でもあるため、私たちはそれらに対してアプリケーションコード全体に適用するのと同じ基準を適用します。

すべてのコードと同様に、テストもメンテナンスが必要です。テストを持つためだけにテストを書くことが目的ではありません。むしろ、期待される動作と予期しない動作をカバーし、迅速な実行とコードのメンテナンスの間で適切なバランスを取ることを目指すべきです。

テストを書く際には、次の点を考慮してください:

  • どの動作をテストしていますか?
  • このコードを実行したときに発生する可能性のあるエラーは何ですか?
  • テストは私たちが考えていることをテストしていますか?それとも偽のポジティブ/ネガティブを導入していますか?
  • 読みやすいですか?他の貢献者は、対応するテストを見て私たちのコードがどのように動作するかを理解できますか?

JavaScriptのテスト

JavaScriptのテストは、テストランナーとしてJestを使用し、グローバルのAPI(describetestbeforeEachなど)アサーションモックスパイ、およびモック関数を使用します。必要に応じて、ReactコンポーネントのテストにはReact Testing Libraryを使用することもできます。

過去には、ReactコンポーネントはEnzymeでユニットテストされていましたが、現在はすべての既存および新しいテストにReact Testing Library (RTL)が使用されています。

Nodeとプロジェクトの依存関係をインストールするための指示に従ったと仮定すると、テストはNPMを使用してコマンドラインから実行できます:

  1. npm test

リントは、コーディング標準を強制し、潜在的なエラーを回避するために使用される静的コード分析です。このプロジェクトでは、ESLintTypeScriptのJavaScript型チェックを使用してこれらの問題をキャッチします。上記のnpm testはユニットテストとコードリントの両方を実行しますが、コードリントはnpm run lintを実行することで独立して確認できます。一部のJavaScriptの問題は、npm run lint:js:fixを実行することで自動的に修正できます。

開発者のワークフローを改善するために、エディタのリント統合を設定するべきです。詳細については、はじめにのドキュメントを参照してください。

リントなしでユニットテストのみを実行するには、npm run test:unitを使用してください。

フォルダ構造

テストは作業ディレクトリのtestフォルダに保管してください。テストファイルはテスト対象ファイルと同じ名前であるべきです。

  1. +-- test
  2. | +-- bar.js
  3. +-- bar.js

テストファイル(少なくとも1つのテストケースを含む)は/testの直下にのみ存在する必要があります。外部モックやフィクスチャを追加する必要がある場合は、サブフォルダに配置してください。例えば:

  • test/mocks/[file-name].js
  • test/fixtures/[file-name].js

テストのインポート

前述のフォルダ構造を考慮して、テストしているコードをインポートする際には、プロジェクトパスを使用するのではなく、相対パスを使用するようにしてください。

良い例

import { bar } from '../bar';

あまり良くない例

import { bar } from 'components/foo/bar';

アプリケーションディレクトリ内の別の位置にコードを移動することにした場合、これによりあなたの生活が楽になります。

テストの説明

  1. テストケースでは、期待される動作を平易な言葉で説明するようにしてください。UIコンポーネントの場合、これはユーザーの視点から期待される動作を説明することを含むかもしれません。
  2. **良い例**
  3. ``````bash
  4. describe( 'CheckboxWithLabel', () => {
  5. test( 'checking checkbox should disable the form submit button', () => {
  6. ...
  7. } );
  8. } );
  9. `

あまり良くない例

  1. describe( 'CheckboxWithLabel', () => {
  2. test( 'checking checkbox should set this.state.disableButton to `true`', () => {
  3. ...
  4. } );
  5. } );

セットアップとティアダウンメソッド

Jest APIには、各テストまたはすべてのテスト、または特定のdescribeブロック内のテストのにタスクを実行できる便利なセットアップとティアダウンメソッドが含まれています。

これらのメソッドは、通常インラインで行うことができないセットアップを可能にするために非同期コードを処理できます。個別のテストケースと同様に、Promiseを返すことができ、Jestはそれが解決されるのを待ちます:

  1. // one-time setup for *all* tests
  2. beforeAll( () =>
  3. someAsyncAction().then( ( resp ) => {
  4. window.someGlobal = resp;
  5. } )
  6. );
  7. // one-time teardown for *all* tests
  8. afterAll( () => {
  9. window.someGlobal = null;
  10. } );
  1. アサーションの後にクリーンアップコードを配置することは避けてください。なぜなら、もしそれらのテストのいずれかが失敗した場合、クリーンアップは行われず、無関係なテストで失敗を引き起こす可能性があるからです。
  2. <a name="mocking-dependencies"></a>
  3. ### 依存関係のモック
  4. #### 依存性注入
  5. 関数に引数として依存関係を渡すことで、コードをテストしやすくすることができます。可能な限り、高いスコープで依存関係を参照することは避けてください。
  6. **あまり良くない例**
  7. ``````bash
  8. import VALID_VALUES_LIST from './constants';
  9. function isValueValid( value ) {
  10. return VALID_VALUES_LIST.includes( value );
  11. }
  12. `

ここでは、VALID_VALUES_LISTから値をインポートして使用する必要があります。

expect( isValueValid( VALID_VALUES_LIST[ 0 ] ) ).toBe( true );

上記のアサーションは、2つの動作をテストしています:1)関数がリスト内のアイテムを検出できること、2)VALID_VALUES_LIST内のアイテムを検出できることです。

しかし、VALID_VALUES_LISTに何が保存されているか、またはリストがHTTPリクエストを介して取得される場合、isValueValidがリスト内のアイテムを検出できるかどうかだけをテストしたい場合はどうでしょうか?

良い例

  1. function isValueValid( value, validValuesList = [] ) {
  2. return validValuesList.includes( value );
  3. }

リストを引数として渡すことで、テスト内でモックvalidValuesList値を渡すことができ、さらにいくつかのシナリオをテストできます:

expect( isValueValid( 'hulk', [ 'batman', 'superman' ] ) ).toBe( false );

expect( isValueValid( 'hulk', null ) ).toBe( false );

expect( isValueValid( 'hulk', [] ) ).toBe( false );

expect( isValueValid( 'hulk', [ 'iron man', 'hulk' ] ) ).toBe( true );

インポートされた依存関係

私たちのコードは、インポートされた外部および内部ライブラリのメソッドやプロパティを複数の場所で使用することが多く、引数を渡すのが煩雑で実用的ではありません。これらのケースでは、jest.mockがこれらの依存関係をスタブするための便利な方法を提供します。

例えば、configモジュールが多くの機能をフィーチャーフラグを介して制御することを想定しましょう。

  1. // bilbo.js
  2. import config from 'config';
  3. export const isBilboVisible = () =>
  4. config.isEnabled( 'the-ring' ) ? false : true;

各条件下での動作をテストするために、設定オブジェクトをスタブし、isEnabledの戻り値を制御するためにjestモッキング関数を使用します。

  1. // test/bilbo.js
  2. import { isEnabled } from 'config';
  3. import { isBilboVisible } from '../bilbo';
  4. jest.mock( 'config', () => ( {
  5. // bilbo is visible by default
  6. isEnabled: jest.fn( () => false ),
  7. } ) );
  8. describe( 'The bilbo module', () => {
  9. test( 'bilbo should be visible by default', () => {
  10. expect( isBilboVisible() ).toBe( true );
  11. } );
  12. test( 'bilbo should be invisible when the `the-ring` config feature flag is enabled', () => {
  13. isEnabled.mockImplementationOnce( ( name ) => name === 'the-ring' );
  14. expect( isBilboVisible() ).toBe( false );
  15. } );
  16. } );

グローバルのテスト

グローバルメソッドを呼び出すコードをテストするためにJestスパイを使用できます。

  1. import { myModuleFunctionThatOpensANewWindow } from '../my-module';
  2. describe( 'my module', () => {
  3. beforeAll( () => {
  4. jest.spyOn( global, 'open' ).mockImplementation( () => true );
  5. } );
  6. test( 'something', () => {
  7. myModuleFunctionThatOpensANewWindow();
  8. expect( global.open ).toHaveBeenCalled();
  9. } );
  10. } );

ユーザーインタラクション

ユーザーインタラクションをシミュレートすることは、ユーザーの視点からテストを書く素晴らしい方法であり、したがって実装の詳細をテストすることを避けることができます。

Testing Libraryを使用してテストを書く際には、ユーザーインタラクションをシミュレートするための2つの主な代替手段があります:

  • 1. fireEvent API、Testing LibraryのコアAPIの一部であるDOMイベントを発火させるためのユーティリティ。
  • 2. user-eventライブラリ、ブラウザでインタラクションが行われた場合に発生するイベントをディスパッチすることによってユーザーインタラクションをシミュレートするTesting Libraryの補助ライブラリ。

組み込みのfireEventは、DOMイベントをディスパッチするためのユーティリティです。これは、テスト仕様で説明されているイベントを正確にディスパッチします。実際のブラウザでのインタラクションでその正確なイベントがディスパッチされなかった場合でもです。

一方、user-eventライブラリは、ユーザーがドキュメントと対話した場合に発生するようにイベントをディスパッチする高レベルのメソッド(例:typeselectOptionscleardoubleClick…)を公開し、React特有の特異性に対処します。

上記の理由から、ユーザーインタラクションのテストを書く際にはuser-eventライブラリが推奨されます

あまり良くない例fireEventを使用してDOMイベントをディスパッチすること。

  1. import { render, screen } from '@testing-library/react';
  2. test( 'fires onChange when a new value is typed', () => {
  3. const spyOnChange = jest.fn();
  4. // A component with one `input` and one `select`.
  5. render( <MyComponent onChange={ spyOnChange } /> );
  6. const input = screen.getByRole( 'textbox' );
  7. input.focus();
  8. // No clicks, no key events.
  9. fireEvent.change( input, { target: { value: 62 } } );
  10. // The `onChange` callback gets called once with '62' as the argument.
  11. expect( spyOnChange ).toHaveBeenCalledTimes( 1 );
  12. const select = screen.getByRole( 'listbox' );
  13. select.focus();
  14. // No pointer events dispatched.
  15. fireEvent.change( select, { target: { value: 'optionValue' } } );
  16. // ...

良い例user-eventを使用してユーザーイベントをシミュレートすること。

  1. import { render, screen } from '@testing-library/react';
  2. import userEvent from '@testing-library/user-event';
  3. test( 'fires onChange when a new value is typed', async () => {
  4. const user = userEvent.setup();
  5. const spyOnChange = jest.fn();
  6. // A component with one `input` and one `select`.
  7. render( <MyComponent onChange={ spyOnChange } /> );
  8. const input = screen.getByRole( 'textbox' );
  9. // Focus the element, select and delete all its contents.
  10. await user.clear( input );
  11. // Click the element, type each character separately (generating keydown,
  12. // keypress and keyup events).
  13. await user.type( input, '62' );
  14. // The `onChange` callback gets called 3 times with the following arguments:
  15. // - 1: clear ('')
  16. // - 2: '6'
  17. // - 3: '62'
  18. expect( spyOnChange ).toHaveBeenCalledTimes( 3 );
  19. const select = screen.getByRole( 'listbox' );
  20. // Dispatches events for focus, pointer, mouse, click and change.
  21. await user.selectOptions( select, [ 'optionValue' ] );
  22. // ...
  23. } );

ブロックUIの統合テスト

統合テストは、異なる部分がグループとしてテストされるテストの一種として定義されます。この場合、テストしたい部分は、特定のブロックまたはエディターロジックのためにレンダリングされる必要がある異なるコンポーネントです。最終的には、これらはユニットテストと非常に似ており、Jestライブラリを使用して同じコマンドで実行されます。主な違いは、統合テストではブロックがspecial instance of the block editor内で実行されることです。

このアプローチの利点は、ブロックエディタの機能の大部分(ブロックツールバーやインスペクターパネルのインタラクションなど)を、完全なe2eテストフレームワークを起動することなくテストできることです。これにより、テストははるかに迅速かつ信頼性高く実行できます。ブロックのUI機能の可能な限り多くを統合テストでカバーし、完全なブラウザ環境を必要とするインタラクションにはe2eテストを使用することが推奨されます。例:ファイルのアップロード、ドラッグアンドドロップなど。

The Cover blockは、このレベルのテストを使用してエディタのインタラクションの大部分をカバーするブロックの例です。

統合テスト用のjestファイルを設定するには:

  1. import { initializeEditor } from 'test/integration/helpers/integration-test-editor';
  2. async function setup( attributes ) {
  3. const testBlock = { name: 'core/cover', attributes };
  4. return initializeEditor( testBlock );
  5. }

initializeEditor関数は、@testing-library/react renderメソッドの出力を返します。また、複数のブロックでエディタを設定できるように、ブロックメタデータオブジェクトの配列も受け入れます。

統合テストエディタモジュールは、ブロックラッパーのaria-labelによってテストされるブロックを選択するために使用できるselectBlockもエクスポートします。例:「ブロック:カバー」。

スナップショットテスト

スナップショットテストの概要と、スナップショットテストを最大限に活用する方法について説明します。

TL;DR 壊れたスナップショット

スナップショットテストが失敗した場合、それはコンポーネントのレンダリングが変更されたことを意味します。それが意図しないものであれば、スナップショットテストはバグを防いだことになります!😊

ただし、変更が意図的であった場合は、スナップショットを更新するために次の手順に従ってください。スナップショットを更新するには、次のコマンドを実行します:

  1. # --testPathPatternはオプションですが、一致するテストのみを実行することではるかに高速になります
  2. npm run test:unit -- --updateSnapshot --testPathPattern path/to/tests
  3. # e2eテストのスナップショットを更新
  4. npm run test:e2e -- --update-snapshots path/to/spec
  • 1. 差分を確認し、変更が予期されるものであることを確認します。
  • 2. コミットします。

スナップショットとは何ですか?

スナップショットは、テストによって生成されたデータ構造の表現に過ぎません。スナップショットはファイルに保存され、テストと一緒にコミットされます。テストが実行されると、生成されたデータ構造がファイル上のスナップショットと比較されます。

スナップショットを作成するのは非常に簡単です:

  1. test( 'foobar test', () => {
  2. const foobar = { foo: 'bar' };
  3. expect( foobar ).toMatchSnapshot();
  4. } );

これが生成されたスナップショットです:

  1. exports[ `test foobar test 1` ] = `
  2. Object {
  3. "foo": "bar",
  4. }

スナップショットを直接作成または変更することは決して行わないでください。スナップショットはテストによって生成および更新されます。

利点

  • テストを追加するのが簡単で簡潔です。
  • 意図しない変更から保護します。
  • 簡単に扱えます。
  • アプリケーションを実行せずに内部構造を明らかにします。

欠点

  • 表現力がありません。
  • 変更が導入されたときにのみ問題をキャッチします。
  • 非決定的なものには問題があります。

ユースケース

スナップショットは主にコンポーネントテストを対象としています。コンポーネントの構造の変更に気づかせてくれるため、リファクタリングに理想的です。スナップショットが一連のコミットの過程で最新の状態に保たれている場合、スナップショットの差分はコンポーネントの構造の進化を記録します。素晴らしいですね!😎

  1. import { render, screen } from '@testing-library/react';
  2. import SolarSystem from 'solar-system';
  3. describe( 'SolarSystem', () => {
  4. test( 'should render', () => {
  5. const { container } = render( <SolarSystem /> );
  6. expect( container ).toMatchSnapshot();
  7. } );
  8. test( 'should contain mars if planets is true', () => {
  9. const { container } = render( <SolarSystem planets /> );
  10. expect( container ).toMatchSnapshot();
  11. expect( screen.getByText( /mars/i ) ).toBeInTheDocument();
  12. } );
  13. } );

リデューサーテストもスナップショットに非常に適しています。リデューサーは、予期せずに変更されるべきではない大きく複雑なデータ構造であり、まさにスナップショットが得意とするものです!

スナップショットの操作

スナップショットが一致しないとCIテストが失敗することに驚くかもしれません。変更が予期される場合は、スナップショットを更新する必要があります。迅速かつ簡単な解決策は、--updateSnapshotを使用してJestを呼び出すことです。これは次のように行うことができます:

  1. npm run test:unit -- --updateSnapshot --testPathPattern path/to/tests
  1. 作業中は`````npm run test:unit:watch`````をバックグラウンドで実行しておくのが良いアイデアです。Jestは変更されたファイルに対してのみ関連するテストを実行し、スナップショットテストが失敗した場合は、`````u`````を押してスナップショットを更新してください!
  2. #### 痛点
  3. 非決定的なテストは一貫したスナップショットを作成しない可能性があるため、注意が必要です。ランダムなもの、時間に基づくもの、またはその他の非決定的なものを扱う場合、スナップショットは問題を引き起こす可能性があります。
  4. 接続されたコンポーネントは扱いが難しいです。接続されたコンポーネントをスナップショットするには、接続されていないコンポーネントをエクスポートする必要があります:
  5. ``````bash
  6. // my-component.js
  7. export { MyComponent };
  8. export default connect( mapStateToProps )( MyComponent );
  9. // test/my-component.js
  10. import { MyComponent } from '..';
  11. // run those MyComponent tests…
  12. `

接続されたプロパティは手動で提供する必要があります。これは、接続された状態を監査する良い機会です。

ベストプラクティス

リファクタリングを開始する場合、スナップショットは非常に便利です。ブランチの最初のコミットとして追加し、進化を見守ることができます。

スナップショット自体は、私たちが期待することについて何も表現しません。スナップショットは、上記の例のように、私たちの期待を説明する他のテストと組み合わせて使用するのが最適です。

もう1つの良い手法は、[toMatchDiffSnapshot]関数(snapshot-diffパッケージによって提供される)を使用することで、DOMの2つの異なる状態間の差分のみをスナップショットすることができます。このアプローチは、プロパティの変更が結果のDOMに与える影響をテストするのに役立ち、はるかに小さなスナップショットを生成します。次の例のように:

  1. test( 'should render a darker background when isShady is true', () => {
  2. const { container } = render( <CardBody>Body</CardBody> );
  3. const { container: containerShady } = render(
  4. <CardBody isShady>Body</CardBody>
  5. );
  6. expect( container ).toMatchDiffSnapshot( containerShady );
  7. } );

同様に、toMatchStyleDiffSnapshot関数は、コンポーネントの2つの異なる状態に関連するスタイルの差分のみをスナップショットすることを可能にします。次の例のように:

  1. test( 'should render margin', () => {
  2. const { container: spacer } = render( <Spacer /> );
  3. const { container: spacerWithMargin } = render( <Spacer margin={ 5 } /> );
  4. expect( spacerWithMargin ).toMatchStyleDiffSnapshot( spacer );
  5. } );

トラブルシューティング

時々、いくつかのストーリーで使用されるrefsをモックする必要があります。詳細を学ぶには、次のドキュメントを確認してください:

その場合、TypeErrorからプロパティにアクセスしようとする行で、Jestによってテストの失敗とTypeErrorが報告されることがあります。

Jestユニットテストのデバッグ

npm run test:unit:debugを実行すると、デバッグモードでテストが開始され、ノードインスペクタクライアントがプロセスに接続して実行を検査できます。Google ChromeまたはVisual Studio Codeをインスペクタクライアントとして使用するための手順は、wp-scriptsドキュメントに記載されています。

ネイティブモバイルテスト

ユニットテストスイートの一部は、React Nativeで開発されたネイティブモバイルコードパスを実行する一連のJestテストです。これらのテストはNode上で実行されるため、特定のネイティブAndroidまたはiOS開発ツールやSDKなしで、開発マシン上でローカルに起動できます。また、通常の開発ツールを使用してデバッグすることもできます。デバッグ方法については、以下をお読みください。

ネイティブモバイルユニットテストのデバッグ

ローカルでテストをデバッグモードで実行するには、次の手順に従ってください:

  • 1. すべてのパッケージをインストールするためにnpm ciを実行したことを確認してください。
  • 2. CLIでGutenbergのルートフォルダ内でnpm run test:native:debugを実行します。Nodeはデバッガーの接続を待っています。
  • 3. Chromeでchrome://inspectを開きます。
  • 4. 「リモートターゲット」セクションで、../../node_modules/.bin/jestターゲットを探し、「検査」リンクをクリックします。これにより、新しいウィンドウが開き、Chrome DevToolsデバッガーがプロセスに接続され、jest.jsファイルの先頭で停止します。ターゲットが表示されない場合は、同じページのOpen dedicated DevTools for Nodeリンクをクリックしてください。
  • 5. コード全体、テストコードを含めて、ブレークポイントやdebugger;ステートメントを配置して停止し、検査します。
  • 6. 「再生」ボタンをクリックして実行を再開します。
  • 7. ネイティブモバイルユニットテストのデバッグを楽しんでください!

ネイティブモバイルのエンドツーエンドテスト

Gutenbergの貢献者は、PRにネイティブモバイルのE2Eテストを実行する継続的インテグレーションE2Eテストが含まれていることに気付くでしょう。失敗したテストのトラブルシューティングについては、継続的インテグレーションにおけるネイティブモバイルテストに関するガイドを確認してください。これらのテストをローカルで実行する方法についての詳細は、こちらにあります。

ネイティブモバイル統合テスト

ネイティブモバイルプロジェクトに統合テストを追加するための取り組みが進行中で、react-native-testing-libraryライブラリを使用しています。統合テストの作成に関するガイドはこちらにあります。

エンドツーエンドテスト

現在存在するほとんどのエンドツーエンドテストは、PuppeteerをヘッドレスChromiumドライバーとして使用してpackages/e2e-testsでテストを実行し、その他はJestテストランナーによって実行されます。

PuppeteerからPlaywrightへの移行プロジェクトが進行中です。可能な限り新しいe2eテストはPlaywrightで作成することをお勧めします。以下のセクションは、主に古いJest + Puppeteerフレームワークに適用されます。Playwrightでテストを書く場合は、専用のガイドを参照してください。

wp-envの使用

組み込みのローカル環境を使用している場合、次のコマンドを使用してローカルでe2eテストを実行できます:

  1. npm run test:e2e

またはインタラクティブに

  1. npm run test:e2e:watch

テストを実行している間にブラウザを観察することが便利な場合があります。その場合は、このコマンドを使用します:

  1. npm run test:e2e:watch -- --puppeteer-interactive
  1. ``````bash
  2. npm run test:e2e:watch -- --puppeteer-interactive --puppeteer-slowmo=200
  3. `

さらに、ブラウザでインタラクティブデバッグのためにdevtoolsを自動的に開くこともできます:

  1. npm run test:e2e:watch -- --puppeteer-devtools

代替環境の使用

  1. ``````bash
  2. ln -s gutenberg/packages/e2e-tests/plugins/* .
  3. `

その後、テストを実行するには、サイトのベースURL、ユーザー名、およびパスワードを指定します。たとえば、テストサイトがhttp://wp.testの場合、次のようにします:

  1. WP_BASE_URL=http://wp.test npm run test:e2e -- --wordpress-username=admin --wordpress-password=password

シナリオテスト

エンドツーエンドテストがローカルで成功するが、GitHub Actionsで失敗する場合、遅いCPUまたはネットワークをシミュレートすることでCPUまたはネットワークに依存するレース条件を特定できるかもしれません:

  1. THROTTLE_CPU=4 npm run test:e2e
  1. [Chromeドキュメント:setCPUThrottlingRate](https://chromedevtools.github.io/devtools-protocol/tot/Emulation#method-setCPUThrottlingRate)を参照してください。
  2. ``````bash
  3. SLOW_NETWORK=true npm run test:e2e
  4. `

SLOW_NETWORKは、Chrome devtoolsで「Fast 3G」に相当するネットワーク速度をエミュレートします。

Chromeドキュメント:emulateNetworkConditionsおよびNetworkManager.jsを参照してください。

  1. OFFLINE=true npm run test:e2e
  1. [Chromeドキュメント:emulateNetworkConditions](https://chromedevtools.github.io/devtools-protocol/tot/Network#method-emulateNetworkConditions)を参照してください。
  2. <a name="core-block-testing"></a>
  3. ### コアブロックテスト
  4. すべてのコアブロックには、主要な保存機能のための少なくとも1セットのフィクスチャファイルと、各非推奨のための1セットのフィクスチャファイルが必要です。これらのフィクスチャは、ブロックの解析とシリアル化をテストします。詳細と手順については、[統合テストフィクスチャのREADME](https://github.com/wordpress/gutenberg/blob/HEAD/test/integration/fixtures/blocks/README.md)を参照してください。
  5. <a name="flaky-tests"></a>
  6. ### フレキテスト
  7. テストが**フレキ**であると見なされるのは、コードの変更なしに複数の再試行で合格したり失敗したりする場合です。CIでは、失敗したテストを自動的に**2回**再試行して、GitHubの問題に自動的に報告します。[`````[Type] Flaky Test`````](https://github.com/WordPress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22%5BType%5D+Flaky+Test%22)ラベルを介して[`````report-flaky-tests`````](https://github.com/WordPress/gutenberg/tree/trunk/packages/report-flaky-tests)GitHubアクションを使用します。連続して3回失敗したテストはフレキテストとしてカウントされず、問題として報告されません。
  8. <a name="php-testing"></a>
  9. ## PHPテスト
  10. PHPのテストは、テストフレームワークとして[PHPUnit](https://phpunit.de/)を使用します。組み込みの[ローカル環境](9a8e7ea323b8b66b.md#local-environment)を使用している場合、次のコマンドを使用してローカルでPHPテストを実行できます:
  11. ``````bash
  12. npm run test:php
  13. `

ファイルが変更されたときに自動的にテストを再実行するには(Jestに似て)、次のコマンドを実行します:

  1. npm run test:php:watch

注:phpunitコマンドはwp-envが実行中であり、composer依存関係がインストールされている必要があります。パッケージスクリプトは、実行中でない場合はwp-envを開始します。

他の環境では、composer run testおよびcomposer run test:watchを実行します。

PHPのコードスタイルは、PHP_CodeSnifferを使用して強制されます。PHP_CodeSnifferとWordPress Coding Standards for PHP_CodeSnifferルールセットをComposerを使用してインストールすることをお勧めします。Composerがインストールされている場合、プロジェクトディレクトリからcomposer installを実行して依存関係をインストールします。上記のnpm run test:phpは、ユニットテストとコードリントの両方を実行します。コードリントは、npm run lint:phpを実行することで独立して確認できます。

リントなしでユニットテストのみを実行するには、npm run test:unit:phpを使用してください。

パフォーマンステスト

機能を追加するにつれてエディタがパフォーマンスを維持することを確認するために、プルリクエストやリリースがいくつかの重要な指標に与える影響を監視します。これには、

  • エディタの読み込みにかかる時間。
  • タイピング時にブラウザが応答するまでの時間。
  • ブロックを選択するのにかかる時間。

パフォーマンステストは、エディタを実行し、これらの測定値をキャプチャするエンドツーエンドテストです。e2eテスト環境が準備されていることを確認してください。

e2eテスト環境を設定するには、Gutenbergリポジトリをチェックアウトし、テストしたいブランチに切り替えます。次のコマンドを実行して環境を準備します。

  1. nvm use && npm install
  2. npm run build:packages

テストを実行するには、次のコマンドを実行します:

  1. npm run test:performance

これにより、実行環境の現在のブランチ/コードの結果が得られます。

さらに、次のコマンドを実行することで、ブランチ(またはタグやコミット)間のメトリックを比較することもできます。./bin/plugin/cli.js perf [branches]、例:

  1. ./bin/plugin/cli.js perf trunk v8.1.0 v8.0.0

最後に、追加の--tests-branch引数を渡して、どのブランチのパフォーマンステストファイルを実行するかを指定できます。これは、パフォーマンステストを変更/拡張する際に特に便利です:

  1. ./bin/plugin/cli.js perf trunk v8.1.0 v8.0.0 --tests-branch add/perf-tests-coverage

このコマンドはベンチマークを実行するのに時間がかかる場合があります。実行中は、コンピュータを使用したり、多くのバックグラウンドプロセスを実行したりして、ブランチ間の結果に影響を与える外部要因を最小限に抑えるようにしてください。