Joomla! と Cypress を使用したエンドツーエンドのテスト - 私の最初のステップと感想

自動テストは、大規模プロジェクトのソフトウェア開発者にとって特別なツールではありません。特に拡張機能の場合、自動テストは問題を迅速に特定するのに役立ちます。拡張機能が新しい Joomla バージョンでスムーズに動作することを確認するのに役立ちます。

Joomla Core 開発者は、サードパーティ ソフトウェア開発者に拡張機能をテストして、ユーザーがバグを見つける前にバグを見つけてもらいたいと考えています。これには多くの時間がかかり、退屈な作業です。したがって、実行されないことがよくあります。特に、実行する必要がある場合はそうではありません。新しいリリースごとに人間によって手動で行われます。自動化されたテストにより、人間が自分で手順を実行することなく、リリースごとに手動の手順を繰り返すことができます。このようにして、ユーザーがライブ システムにアクセスするときにバグに遭遇する前にバグを発見できます。

ところで、Cypress をチェックアウトしたい人は、このテキストを始めるのに適しています。すべてを自分でインストールして設定することなく、問題をテストできます。Joomla プロジェクトの Github リポジトリでは、すべての準備が整っています。

イントロ

「品質をテストできないのは事実ですが、テストなしでは高品質のものを開発することが不可能であることも同様に明白です。」 – [James A. Whittaker]

Cypress に出会うまでは、テストの邪魔になることが多かった障壁が実際にいくらか取り除かれるとは想像もできませんでした。ソフトウェアのテストに多くの時間を費やし、以前は問題に対処するのにさらに多くの時間を費やしました。これはテストの不足が原因で発生しました!今では、次のようなテストが必要であると確信しています。

  • プログラムにできるだけ間に合うように、
  • 自動的、
  • 頻繁に - 理想的にはプログラムを変更するたびに、

    コスト以上の成果をもたらします。さらに、テストは楽しいものですらあります。

テスト方法を学ぶ価値はあります!テスト方法は、どのプログラミング言語でも使用できるだけでなく、ほとんどすべての人間の作業に適用できるため、長期にわたって使用できます。人生において重要なことはほとんどすべて、時々テストする必要があります。テスト手法は特定のソフトウェア ツールに依存しないため、流行り廃りが頻繁にあるプログラミング手法やプログラミング言語とは異なり、優れたテストを設定する方法に関する知識は時代を超えています。

誰がこの文章を読むべきですか?

ソフトウェアのテストは時間の無駄だと考えている人は誰でも、この記事を読んでください。特に、ソフトウェアのテストを書きたいと常に思っていたが、さまざまなテストを書いたことはなかった開発者に読んでもらいたいと思います。サイプレスは、そのような障壁を取り除く方法になる可能性があります。

ある理論

魔法のトライアングル

マジックトライアングルは、コスト、所要時間、達成可能な品質の関係を表します。もともと、この関係はプロジェクト管理で認識され、説明されていました。しかし、おそらく他の分野でもこの緊張関係について聞いたことがあるでしょう。ほぼすべての運用上の問題です。会社内のプロセス。

たとえば、一般に、コストが高いほど、品質や完了日、つまり時間にプラスの影響があると考えられています。

 

プロジェクト管理における魔法の三角形 - プロジェクトにより多くの資金が投資されると、品質や時間にプラスの影響を与えます。

逆に、コストを削減すると、品質が低下したり、完成が遅れたりすることになります。

プロジェクト管理における魔法の三角形 - プロジェクトに投資される資金が少ないと、品質や時間に悪影響を及ぼします。

ここで魔法が始まります: 時間、コスト、品質の間の相関関係を克服します! なぜなら、長期的には、この問題は実際に克服できるからです。

時間、コスト、品質の関係は長期的には克服できます。

おそらく皆さんも、品質の低下が長期的にはコスト削減につながらないことを実際に経験したことがあるのではないでしょうか。これによって生じる技術的負債により、コストの増加や追加の時間さえも発生することがよくあります。

長期的には、コスト、時間、品質の相関関係は実際に克服できます。

技術的負債とは、良く書かれたソフトウェアと比較して、良くないプログラムのソフトウェアに変更や機能拡張を加える際に生じる余分な労力を指します。マーティン・ファウラー氏は、技術的負債の次の種類を区別しています: 意図的に生じたものと不注意で生じたもの彼はまた、技術的負債が賢明なものと無謀なものとを区別しています。

技術的負債

コストとメリット

文献には、ソフトウェア プロジェクトの成功の可能性に関する壊滅的な統計が見つかります。1990 年代に AW Feyhl による研究ですでに記録されていた否定的なイメージはほとんど変わっていません。ここでは、50 の組織の 162 のプロジェクトを分析しています。 、元の計画と比較したコストの偏差が決定されました: プロジェクトの 70% で少なくとも 50% のコストの偏差が示されました! 何かが間違っています! それをそのまま受け入れることはできませんよね?

1 つの解決策は、コスト見積もりを完全に省略し、 #NoEstimates 運動の議論に従うことです。この運動は、ソフトウェア プロジェクトのコスト見積もりはナンセンスであるという意見です。#NoEstimates の意見によれば、ソフトウェア プロジェクトには常に生産が含まれています新しいものは既存の経験と比較できないため、予測できません。

経験を積めば積むほど、極端な意見はよくないという結論に達します。解決策はほとんどの場合中間にあります。ソフトウェア プロジェクトでも同様に極端な意見は避け、中間を探してください。100% でな​​ければなりません。確かな計画を立ててください。しかし、新しいプロジェクトを無邪気に始めるべきではありません。ソフトウェア プロジェクトの管理、特にコストの見積もりは重要なトピックですが、このテキストではこれ以上退屈させません。この記事の焦点は、E2E がどのように行われるかを示すことです。テストはソフトウェア開発の実際のワークフローに統合できます。

ソフトウェアテストをワークフローに統合する

ソフトウェアをテストすることに決めました。すばらしいですね! これを行うのに最適な時期はいつですか? プロジェクトのさまざまなフェーズでバグを修正するコストを見てみましょう。バグを早く見つけるほど、修正コストは低くなります。 。

プロジェクトのさまざまな段階におけるトラブルシューティングにかかる​​相対コスト

テストとデバッグ: 頻繁に同じ言葉で言及され、その意味が同じである言葉があります。しかし、よく見ると、これらの用語は異なる解釈を表します。テストとデバッグはこれらの言葉に属します。この 2 つの用語には共通点があります。 「故障を検知する」という意味ですが、意味にも違いがあります。

  • テストでは開発中に未知の誤動作が検出されますが、誤動作の発見には費用がかかりますが、エラーの位置特定と除去には低コストがかかります。
  • デバッガは、製品の完成後に見つかった不具合を修正します。不具合の発見には無料ですが、エラーの特定と修正には費用がかかります。

結論: できるだけ早く統合テストを開始することが最も合理的ですが、残念ながら、ほとんどがボランティアの貢献者がいる Joomla のようなオープンソース プロジェクトでこれを実装するのは困難です。

継続的インテグレーション (CI)
テストの継続的統合

次のシナリオを想像してください。人気のあるコンテンツ管理システムの新しいバージョンが間もなくリリースされます。前回のリリース以降、チームの開発者が貢献してきたすべてのものが、初めて一緒に使用されます。機能しますか? すべてのテストは成功しますか? - プロジェクトにテストが統合されているかどうか. それとも、新しいバージョンのリリースは再び延期されなければならず、神経をすり減らすような何時間ものバグ修正が待っているのでしょうか? ちなみに、リリース日の延期は、製品のイメージにとってもよくありません。ソフトウェア製品です! このシナリオを経験することを好む開発者はいません。ソフトウェア プロジェクトが現在どのような状態にあるのかをいつでも知っておく方がはるかに良いです。既存のコードに適合しないコードは、「作成された」後にのみ統合する必要があります。フィットするように」。特に、セキュリティのギャップを修正する必要があることがますます一般的になっている時代では、プロジェクトは常にリリースを作成できる必要があります! そして、ここで継続的インテグレーションが登場します。

継続的インテグレーションでは、ソフトウェアの個々の要素が永続的に統合されます。ソフトウェアは小さなサイクルで作成およびテストされます。このようにして、統合中に問題が発生したり、テストの欠陥が数日や数週間後ではなく、早い段階で発生したりします。統合が成功すると、エラーはプログラミングの直前に発見され、通常はプログラムのごく一部のみが影響を受けるため、トラブルシューティングがはるかに簡単になります。Joomla は継続的インテグレーションを使用して新しいコードを統合します。新しいコードは、すべてのテストに合格した場合にのみ統合されます。

新しいソフトウェアを継続的に統合すると、エラーはプログラミングの直前に発見され、通常はプログラムのごく一部のみが影響を受けるため、トラブルシューティングがはるかに簡単になります。

継続的統合中に常にすべてのプログラム部分のテストを利用できるようにするには、テスト駆動のソフトウェアを開発する必要があります。

テスト駆動開発 (TDD)

テスト駆動開発は、小さなステップで開発するプログラミング手法です。最初にテスト コードを作成します。その後、テスト対象のプログラム コードを作成します。プログラムへの変更は、その変更のテスト コードが変更された後にのみ行われます。そのため、テストは作成直後に失敗します。必要な機能がまだプログラムに実装されていません。その後初めて、実際のプログラム コード、つまりテストを満たすプログラム コードを作成します。

TDD テストは、プログラムを正しく作成するのに役立ちます。

このテクニックについて初めて聞いたときは、その概念に馴染めないかもしれません。結局のところ、「人間」は常に最初に何か生産的なことをしたいと考えています。そして、テストを書くことは一見生産的とは思えません。試してみてください。時々。新しいテクニックは、それを知って初めて友達になります! テスト カバレッジが高いプロジェクトでは、新しい機能を追加する方が快適です。

本文の最後にある演習部分を読んでいただければ、実際に試してみることができます。まずテストを作成し、次に Joomla Core のコードを書きます。その後、すべてをまとめて Github に PR として送信します。みんながこれをやれば Joomla理想的なテスト範囲が得られます。

行動駆動型開発 (BDD)

BDD は、別のプログラミング手法やテスト手法ではなく、ソフトウェア開発のベスト プラクティスの一種です。BDD は、TDD と一緒に使用するのが理想的です。原則として、動作駆動開発は、プログラム コードの実装ではなく、実行のテストを表します。 - つまり、プログラムの動作 テストでは、仕様、つまり顧客の要件が満たされているかどうかをチェックします。

動作駆動型の方法でソフトウェアを開発する場合、テストはプログラムを正しく書くのに役立つだけでなく、正しいプログラムを書くのにも役立ちます。

「適切なプログラムを作成する」とはどういう意味ですか? ユーザーは開発者とは異なる見方をすることがあります。Joomla で記事を削除するワークフローはその一例です。私は、ページ内のステータス アイコンをクリックするユーザーに何度も遭遇します。ユーザーは通常、アイテムが完全に削除されたと直感的に思いますが、アイテムはゴミ箱からアクティブに切り替わります。開発者にとって、ステータス アイコンをクリックすると、他のすべてのビューでステータスが変更され、切り替えになります。なぜこれがゴミ箱で異なる必要があるのでしょうか? 開発者にとって、関数はエラーなしで実装されています。Joomla は正しく動作します。しかし、私の目から見ると、その関数はその場所では適切なものではありません。なぜなら、ほとんどのユーザーはそれをまったく異なる方法で説明/要求するからです。 。

動作駆動開発では、ソフトウェアの要件をシナリオまたはユーザーストーリーと呼ばれる例を通じて説明します。

  • ソフトウェアの開発プロセスへのエンドユーザーの強い関与、
  • テキスト形式のユーザー ストーリー/事例を含むプロジェクトのすべてのフェーズのドキュメント - 通常は記述言語 Gherkin で記述されます。
  • これらのユーザーストーリー/ケーススタディの自動テスト、
  • したがって、実装するソフトウェアの記述にいつでもアクセスでき、この記述を利用して、実装済みのプログラム コードの正確性を継続的に確認できます。

Joomla プロジェクトはGoogle Summer of Code プロジェクトで BDD を導入しました . プログラミングの知識がないユーザーでもGherkin を使用してより簡単に参加できることが期待されました). このアプローチは一貫してフォローアップされませんでした. 当時、Joomla は Codeception をCypress を使用すると、BDD 開発も BDD 方式で開発できます。

企画

テストの種類
  • 単体テスト: 単体テストは、最小のプログラム単位を独立してテストするテストです。
  • 統合テスト: 統合テストは、個々のユニットの相互作用をテストするテストです。
  • E2E テストまたは受け入れテスト: 受け入れテストでは、プログラムが最初に定義されたタスクを満たしているかどうかを確認します。
戦略

Joomla に新しい機能を追加し、テストで保護したい場合は、2 つの方法で進めることができます。

トップダウンとボトムアップは、複雑な問題を理解して提示するための 2 つの根本的に異なるアプローチです。トップダウンは、抽象的で一般的なものから具体的で具体的なものへと段階的に進みます。これを例で説明すると、Joomla のようなコンテンツ管理システムです。一般的に、Web サイトはブラウザーで表示されますが、具体的には、このプロセスには多数の小さなサブタスクがあり、その 1 つが見出しに特定のテキストを表示するタスクです。

ボトムアップは反対の方向を表します。この時点で、動作駆動型開発の 1 つの要素は、ソフトウェアの動作をテキストで説明することであることをもう一度覚えておく価値があります。この受け入れ基準の説明は、テスト (特にトップ) の作成に役立ちます。 -レベルのエンドツーエンドテストまたは受け入れテスト。

今日のテスト作成の通常のアプローチは、下からのものです。動作駆動型のソフトウェア開発を好む場合は、逆の戦略を使用する必要があります。トップダウン戦略を使用する必要があります。トップダウン戦略では、誤解が早い段階で特定されます。設計段階で。

テスト戦略: トップダウン テストとボトムアップ テスト

  • トップダウン テスト: トップダウン戦略を適用する場合、受け入れテストから開始します。つまり、ユーザー要件に最も密接に関連するシステムの部分から始まります。人間のユーザー向けに作成されたソフトウェアの場合、これは通常ユーザー インターフェイスです。 . 焦点は、ユーザーがシステムとどのように対話するかをテストすることにあります。トップダウン テストの欠点は、テストの複製の作成に多くの時間を費やす必要があることです。まだ統合されていないコンポーネントは、プレースホルダーに置き換える必要があります。最初は実際のプログラムコードではないため、欠落した部分を人工的に作成する必要があり、徐々にその人工データが実際に計算されたデータに置き換えられます。

  • ボトムアップ テスト: ボトムアップ戦略に従う場合は、単体テストから開始します。最初に、開発者はターゲットの状態を念頭に置いています。ただし、その後、最初にこのターゲットを個々のコンポーネントに分割します。ボトムアップ アプローチでは、コンポーネントが後で実際の状況でどのように使用されるかをテストするのが困難です。ボトムアップ テストの利点は、ソフトウェア部品が非常に早く完成することです。ただし、これらの部品の使用には注意が必要です。正しく動作することは単体テストによって保証されますが、最終結果が本当に顧客がソフトウェアとして想像したものであるかどうかは保証されません。

テストピラミッド by マイク・コーン

どのテスト タイプのテストをいくつ実装する必要がありますか? Mike Cohn のテスト ピラミッドは、自動ソフトウェア テストの採用の概念を説明しています。このピラミッドは、使用頻度と関連性に従って構造化された 3 つのレベルで構成されています。

理想的には、テスト ピラミッドの基礎は、迅速で保守が容易な多数の単体テストによって形成され、これにより、ほとんどのエラーを迅速に検出できます。

中間レベルは統合テストです。重要なインターフェイスの対象を絞ったテストのためのサービスを提供します。統合テストの実行時間は単体テストよりも長く、メンテナンスも複雑です。

ピラミッドの最上位は低速な E2E テストで構成されており、多くのメンテナンスが必要になる場合があります。E2E テストは、アプリケーションを完全なシステムとしてテストするのに非常に役立ちます。

要件

次の実践的な部分に取り組むにはどのような機器が必要ですか?

次の実践的な部分に積極的に取り組むために必要な要件は何ですか? このマニュアルの内容に取り組むために、多くの要件を満たす必要はありません。もちろん、コンピュータが必要です。Git を使用した開発環境。 NodeJS と Composer およびローカル Web サーバーがインストールされているか、インストール可能である必要があります。

あなたは個人的にどのような知識を持っておくべきですか?

基本的なプログラミング テクニックを知っておく必要があります。小さな Web アプリケーションをすでにプログラムしているのが理想的です。いずれの場合でも、開発用コンピュータ上のファイルの保存場所と、それらをインターネット ブラウザに読み込む方法を知っておく必要があります。新しいことを始めるには。

試してみてください。テストを次のプロジェクトに統合してください。おそらく、テストの最初の経験によって、退屈なデバッグ セッションや実際のシステムの厄介なバグが回避されるでしょう。結局のところ、テストのセーフティ ネットがあれば、より少ない労力でソフトウェアを開発できます。ストレス。

セットアップ中

Joomla! を使用して Cypress をセットアップする

Github で入手可能な開発者バージョンでは、Joomla は Cypress に対応して構成されています。ガイドとして使用できるテストがすでに用意されています。そのため、最初の概要を把握するためにすべてを自分でセットアップする必要はありません。このようにして、Cypress を試すことができます、その利点と欠点を理解し、テスト ツールを使用するかどうかを自分で決定してください。

ローカル環境をセットアップする手順は次のとおりです。

ローカル Web サーバーのルートにリポジトリのクローンを作成します。

$ git clone https://github.com/joomla/joomla-cms.git

joomla-cms フォルダーに移動します。

$ cd joomla-cms

Joomla ロードマップによると、次のメジャー バージョン 5.0 は 2023 年 10 月にリリースされる予定です。最新の状態にするために、ここではこの開発バージョンを使用します。

ブランチ5.0-devに変更します。

$ git checkout 5.0-dev

必要なすべてのコンポーザー パッケージをインストールします。

$ composer install

必要な npm パッケージをすべてインストールします。

$ npm install

ワークステーションのセットアップに関する詳細とヘルプについては、Joomla ドキュメントの記事「Joomla 開発用のワークステーションのセットアップ」を参照してください。Cypress については、 cypress.ioに情報があります。ただし、この時点では必要ありません。Joomla がすべてをセットアップします。構成ファイルを介して個々のデータを設定するだけですjoomla-cms/cypress.config.js

個別のデータを設定します。このために、方向としてテンプレートを使用できますjoomla-cms/cypress.config.dist.js。私の場合、このファイルは次のようになります:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  fixturesFolder: 'tests/cypress/fixtures',
  videosFolder: 'tests/cypress/output/videos',
  screenshotsFolder: 'tests/cypress/output/screenshots',
  viewportHeight: 1000,
  viewportWidth: 1200,
  e2e: {
    setupNodeEvents(on, config) {},
    baseUrl: 'http://localhost/joomla-cms',
    specPattern: [
      'tests/cypress/integration/install/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/administrator/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}',
      'tests/cypress/integration/site/**/*.cy.{js,jsx,ts,tsx}'
    ],
    supportFile: 'tests/cypress/support/index.js',
    scrollBehavior: 'center',
    browser: 'firefox',
    screenshotOnRunFailure: true,
    video: false
  },
  env: {
    sitename: 'Joomla CMS Test',
    name: 'admin',
    email: このメールアドレスはスパムボットから保護されています。閲覧するにはJavaScriptを有効にする必要があります。',
    username: 'admin',
    password: 'adminadminadmin',
    db_type: 'MySQLi',
    db_host: 'mysql',
    db_name: 'test_joomla',
    db_user: 'root',
    db_password: 'root',
    db_prefix: 'j4_',
  },
})

具体的には、後でtests/cypress/integration/module/**/*.cy.{js,jsx,ts,tsx}モジュールspecPattern のテストをそこに保存したいため、ディレクトリを配列に追加しました。次に、インストールを手動でテストし、自己割り当てしたものをよりよく覚えておきたいため、ユーザー名とパスワードを変更しました。Docker コンテナを使用します。したがって、データベースサーバーとアクセスデータを変更し、最後にhttp://localhost/joomla-cmsJoomla インストールのルート URL を設定する必要がありました。

ヒノキを使用

ウェブブラウザ経由

Joomla ルート ディレクトリで CLI 経由で呼び出しますnpm run cypress:open。しばらくすると、Cypress アプリが開きます。以前にファイルを作成しました。joomla-cms/cypress.config.dist.jsこれが検出されたことは、E2E テストが構成済みとして指定されているという事実からわかります。

96;npm run cypress:open96; を呼び出した後、Cypress アプリが開きます。

ここでは、E2E テストを実行するかどうか、および使用するブラウザを選択できます。例として、「Firefox でテストを開始」オプションを選択しました。

Cypress アプリでの E2E テスト: 使用するブラウザを選択します。

利用可能なすべてのテスト スイートがリストされ、実行するものをクリックできます。テスト スイートを選択すると、テストが実行され、ブラウザでテストの実行をリアルタイムで表示できます。

Cypress アプリ経由の Firefox の Joomla テスト スイート。

テストの実行中、片側に実行されたスクリプトが表示され、右側にブラウザの結果が表示されます。これらは単なるスクリーンショットではなく、その時点でのブラウザの実際のスナップショットであるため、実際の HTML コードを確認できます。 . テストのスクリーンショットやビデオも可能です。

Joomla のインストールテストが進行中です。

as を使用すると、db_host: 'localhost',インストールをテストできるため、このテキストの次の部分で作業できるように Joomla が正しく設定されています。

私のように、外部ソース (lcoalhost ではなく、Docker コンテナーを使用します) を使用する場合db_host、この種のインストールのテストはまだ準備ができていません。その場合、インストール ルーチンにセキュリティに関する問題があります。テストではまだ考慮されていません。この場合、ファイルに入力された情報を使用して Joomla を手動でインストールしますjoomla-cms/cypress.config.js。次のテストでは、この構成ファイルの設定が使用されます (たとえば、Joomla 管理領域へのログインなど)。この方法でテスト開発者は実行しますログイン データの入力を気にする必要はなく、一致するユーザーとパスワードが常に設定ファイルから自動的に使用されます。

ヘッドレス

デフォルトでは、cypress runすべてのテストがheadless で/joomla-cms/tests/cypress/output/screenshots実行されます。次のコマンドは、コーディング済みのテストをすべて実行し、エラーが発生した場合にスクリーンショットをディレクトリに保存します。出力ディレクトリはcypress.config.jsファイルに設定されています。

$ npm run cypress:run

その他の CLI コマンド

package.jsonJoomla プロジェクトにはスクリプトとして実装されていない他の便利なコマンドもありますが、私はnpx [docs.npmjs.com/commands/npx] 経由で実行します。

サイプレス検証

このcypress verifyコマンドは、Cypress が正しくインストールされており、実行できることを確認します。

$ npx cypress verify

✔  Verified Cypress! /.../.cache/Cypress/12.8.1/Cypress
ヒノキ情報

このcypress infoコマンドは、Cypress と現在の環境に関する情報を出力します。

$ npx cypress info
Displaying Cypress info...

Detected 2 browsers installed:

1. Chromium
  - Name: chromium
  - Channel: stable
  - Version: 113.0.5672.126
  - Executable: chromium
  - Profile: /.../snap/chromium/current

2. Firefox
  - Name: firefox
  - Channel: stable
  - Version: 113.0.1
  - Executable: firefox
  - Profile: /.../snap/firefox/current/Cypress/firefox-stable

Note: to run these browsers, pass : to the '--browser' field

Examples:
- cypress run --browser chromium
- cypress run --browser firefox

Learn More: https://on.cypress.io/launching-browsers

Proxy Settings: none detected
Environment Variables: none detected

Application Data: /.../.config/cypress/cy/development
Browser Profiles: /.../.config/cypress/cy/development/browsers
Binary Caches: /.../.cache/Cypress

Cypress Version: 12.8.1 (stable)
System Platform: linux (Ubuntu - 22.04)
System Memory: 4.08 GB free 788 MB
ヒノキバージョン

このcypress versionコマンドは、インストールされている Cypress バイナリ バージョン、Cypress パッケージのバージョン、Cypress の作成に使用された Electron のバージョン、およびバンドルされたノードのバージョンを出力します。

$ npx cypress version
Cypress package version: 12.8.1
Cypress binary version: 12.8.1
Electron version: 21.0.0
Bundled Node version: 16.16.0

Cypress のドキュメントには、より詳細な情報が記載されています。

最初の独自のテストを作成する

これまでのところすべてがうまくいっている場合は、独自のテストの作成を開始できます。

概要を確認する

すでに開発されたテストから学ぶ

Joomla CMS の開発バージョンにはすでに Cypress テストがあり、これらはフォルダーにあります/tests/System/integration。例で学びたい方は、ここで適切な入門書を見つけることができます。

反復的なタスクのコードをインポートする

Joomla 開発者は、一般的なテスト ケースのテスト コードを提供する NodeJsプロジェクト joomla-cypressに取り組んでいます。これらは、CMS の開発者バージョンのインストール中に、npm install経由でインポートされます。

  • package.jsonそして、経由して
  • サポート ファイル/tests/System/support/index.js。サポート ファイルは構成で定義されますcypress.config.js
// package.json
{
  "name": "joomla",
  "version": "5.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0-or-later",
  "repository": {
    "type": "git",
    "url": "https://github.com/joomla/joomla-cms.git"
  },
...
  "devDependencies": {
    ...
    "joomla-cypress": "^0.0.16",
    ...
  }
}

例としては、ツールバー ボタンをクリックすることが挙げられます。たとえば、カスタム テストでCypress.Commands.add('clickToolbarButton', clickToolbarButton)コマンドがclickToolbarButton()使用可能になり、cy.clickToolbarButton('new')ボタンをクリックするとNewシミュレートされます。これに必要なコードは、以下に抜粋したコードに示されています。

// node_modules/joomla-cypress/src/common.js
...
const clickToolbarButton = (button, subselector = null) => {
  cy.log('**Click on a toolbar button**')
  cy.log('Button: ' + button)
  cy.log('Subselector: ' + subselector)

  switch (button.toLowerCase())
  {
    case "new":
      cy.get("#toolbar-new").click()
      break
    case "publish":
      cy.get("#status-group-children-publish").click()
      break
    case "unpublish":
      cy.get("#status-group-children-unpublish").click()
      break
    case "archive":
      cy.get("#status-group-children-archive").click();
      break
    case "check-in":
      cy.get("#status-group-children-checkin").click()
      break
    case "batch":
      cy.get("#status-group-children-batch").click()
      break
    case "rebuild":
      cy.get('#toolbar-refresh button').click()
      break
    case "trash":
      cy.get("#status-group-children-trash").click()
      break
    case "save":
      cy.get("#toolbar-apply").click()
      break
    case "save & close":
      cy.get(".button-save").contains('Save & Close').click()
      break
    case "save & new":
      cy.get("#save-group-children-save-new").click()
      break
    case "cancel":
      cy.get("#toolbar-cancel").click()
      break
    case "options":
      cy.get("#toolbar-options").click()
      break
    case "empty trash":
    case "delete":
      cy.get("#toolbar-delete").click()
      break
    case "feature":
      cy.get("#status-group-children-featured").click()
      break
    case "unfeature":
      cy.get("#status-group-children-unfeatured").click()
      break
    case "action":
      cy.get("#toolbar-status-group").click()
      break
    case "transition":
      cy.get(".button-transition.transition-" + subselector).click()
      break
  }

  cy.log('--Click on a toolbar button--')
}

Cypress.Commands.add('clickToolbarButton', clickToolbarButton)
...

次のコードは、別の例、管理領域へのログインを示しています。

// /node_modules/joomla-cypress/src/user.js
...
const doAdministratorLogin = (user, password, useSnapshot = true) => {
  cy.log('**Do administrator login**')
  cy.log('User: ' + user)
  cy.log('Password: ' + password)

  cy.visit('administrator/index.php')
  cy.get('#mod-login-username').type(user)
  cy.get('#mod-login-password').type(password)
  cy.get('#btn-login-submit').click()
  cy.get('h1.page-title').should('contain', 'Home Dashboard')

  cy.log('--Do administrator login--')
}

Cypress.Commands.add('doAdministratorLogin', doAdministratorLogin)

...
個々の環境での共通タスク

ディレクトリには/tests/System/support、個別の環境で共通のタスクがあります。これらは簡単に再利用できるように、サポート ファイルを介してインポートされます。/tests/System/support/index.js頻繁に繰り返されるタスクの例として、ファイルで処理される管理領域へのログインがあります。/tests/System/support/commands.js関数を使用してdoAdministratorLogin

次のコードは、構成からの情報がcypress.config.jsテストでどのように使用されるかを示しています。には、グループ内のプロパティCypress.env('username')の値が割り当てられます。usernameenv

また、コマンドを上書きする方法もここで確認できます。Cypress.Commands.overwrite('doAdministratorLogin' ...),パッケージで確認したコードを上書きしますjoomla-cypress。利点は、ユーザーとパスワードが個別の設定から自動的に使用されることです。

// /tests/System/support/commands.js
...
Cypress.Commands.overwrite('doAdministratorLogin', (originalFn, username, password, useSnapshot = true) => {
  // Ensure there are valid credentials
  const user = username ?? Cypress.env('username');
  const pw = password ?? Cypress.env('password');

  // Do normal login when no snapshot should be used
  if (!useSnapshot) {
    // Clear the session data
    Cypress.session.clearAllSavedSessions();

    // Call the normal function
    return originalFn(user, pw);
  }

  // Do login through the session
  return cy.session([user, pw, 'back'], () => originalFn(user, pw), { cacheAcrossSpecs: true });
});
...

独自の Joomla 拡張機能をインストールする

独自のコードをテストする方法を確認するために、Joomla バックエンド経由で簡単なサンプル コンポーネントをインストールします。インストール用のファイルはCodebergからダウンロードできます。

独自の Joomla 拡張機能をインストールします。

インストール後、Joomla バックエンドの左側のサイドバーに Foo コンポーネントのビューへのリンクが表示されます。

Joomla バックエンドのサンプル コンポーネントのビュー。

これで、テスト環境がセットアップされ、テスト用のコードが完成しました。

初めての独自テスト

フック

バックエンドをテストするとき、ログインして各テストを開始する必要があることがわかります。関数を使用することで、この冗長なコードを防ぐことができます。このいわゆるフックは、各テストが実行される前に入力したコードを実行します。そのため、 という名前が付けられましbeforeEach()beforeEach()

Cypress は、テスト グループ内のテストの前後に実行される および フック、グループ内の個々のテストの前後に実行される および フックなど、いくつかのタイプのフックを提供します。フックはグローバルに定義することも、特定のブロック内で定義することもできます。ファイル内のコード例では、ブロック内の各テストの前にバックエンドでログインが実行されます。beforeafterbeforeEachafterEachdescribedtests/System/integration/administrator/components/com_foos/FoosList.cy.jsdescribedtest com_foos features

ここで実践的な部分から開始し、tests/System/integration/administrator/components/com_foos/FoosList.cy.js最初の生産的なテストを作成する前に、次のコードが切り取られたファイルを作成します。最初の例では、テストの前にバックエンドに正常にログインできるはずです。これは、最初のテストを作成した後にテストします。

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

})

注: file 内に実装されるフックは/tests/System/support/index.js、テスト スーツ内の各テスト ファイルに適用されます。

テストの成功

テスト用にインストールしたコンポーネントには、AstridNinaの3 つの要素が含まれていますElmar。まず、これらの要素が正常に作成されたかどうかをテストします。

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')

    cy.checkForPhpNoticesOrWarnings()
  })
})

checkForPhpNoticesOrWarnings()注:ファイル内にある関数/node_modules/joomla-cypress/src/support.js

mainCypress コマンドgetを使用してDOM 要素を取得します。

左側のサイドバーにある使用可能なテストのリストに、作成したばかりのテストが表示されますFooList.cy.js。そうでない場合は、ブラウザを閉じてnpm run cypress:open再度実行してください。

Joomla は独自の拡張機能のテストを実行します。

テスト名をクリックして実行すると、正常に終了し、緑色のメッセージが表示されます。

テストが正常に実行された後のビュー。

失敗したテスト

cy.get('main').should('contain.text', 'Sami')実行が失敗するようにテスト ファイルに行を追加します。この名前の要素はありません。テスト ファイルを保存した後、Cypress は変更を認識します。変更するたびに、Cypress はテスト ファイル内のすべてのテストを自動的に再実行します。

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
describe('Test com_foos features', () => {
  beforeEach(() => {
    cy.doAdministratorLogin()
  })

  it('list view shows items', function () {
    cy.visit('administrator/index.php?option=com_foos')

    cy.get('main').should('contain.text', 'Astrid')
    cy.get('main').should('contain.text', 'Nina')
    cy.get('main').should('contain.text', 'Elmar')
    cy.get('main').should('contain.text', 'Sami')

    cy.checkForPhpNoticesOrWarnings()
  })
})

予想通り、テストは失敗しました。赤いメッセージが表示されます。左側のサイドバーに各テスト ステップのコードが表示されます。したがって、エラーの理由を見つけることができます。各ステップには、HTML ドキュメントのスナップショットがあります。これはいつでもマークアップを確認できるため、特に開発中に役立ちます。

テストが失敗した後のビュー。

ファイル内のテストを 1 つだけ実行する

.only()デモ拡張には複数のレイアウトが含まれています。空の状態のレイアウトをテストするためのテストを追加します。このファイルには 2 つのテストがあるため、Cypress はファイルを保存するたびに常に両方のテストを実行します。テストを1 つだけ行うように使用できます。実行されます:

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js

describe('Test com_foos features', () => {
    beforeEach(() => {
        cy.doAdministratorLogin()
    })

    it('list view shows items', function () {
        cy.visit('administrator/index.php?option=com_foos')

        cy.get('main').should('contain.text', 'Astrid')
        cy.get('main').should('contain.text', 'Nina')
        cy.get('main').should('contain.text', 'Elmar')

        cy.checkForPhpNoticesOrWarnings()
    })

    it.only('emptystate layout', function () {
        cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
        cy.get('main').should('contain.text', 'No Foo have been created yet.')
    })
})

開発中、これは非常に便利です。

特別なテスト属性

ここで、コンポーネントのフロントエンドをテストしたいと思います。これを別のファイルで行います/tests/System/integration/site/components/com_foos/FooItem.cy.js

ほとんどの場合、Joomla テストで要素を取得するために CSS クラスを使用します。これは完全に有効で機能しますが、実際には推奨されません。なぜ使用しないのでしょうか? CSS クラスまたは ID を使用するときは、テストを次のものにバインドします。時間の経過とともに変更される可能性が最も高くなります。クラスと ID はデザイン、レイアウト用であり、場合によっては制御用の JavaScript を介して使用されるため、簡単に変更される可能性があります。誰かがクラス名または ID を変更すると、テストは機能しなくなります。テストの脆弱性を軽減し、将来性をさらに高めるため、サイプレスでは、特にテスト目的で要素に特別なデータ属性を作成することをお勧めします。

data-test要素の属性を使用するので、まずdata-test="foo-main"製品コードに属性を追加します。

// /components/com_foos/tmpl/foo/default.php

\defined('_JEXEC') or die;
?>
<div data-test="foo-main">
Hello Foos
</div>

次に、属性を検索して実稼働コードをテストします[data-test="foo-main"]

// tests/System/integration/site/components/com_foos/FooItem.cy.js
describe('Test com_foo frontend', () => {
  it('Show frondend via query in url', function () {
    cy.visit('index.php?option=com_foos&view=foo')

    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')

    cy.checkForPhpNoticesOrWarnings()
  })
})
メニュー項目のテストと、イベント、待ち時間、ベスト プラクティスについての考え

ここで、コンポーネントのメニュー項目の作成をテストしたいと思います。これを別のファイルで実行します/tests/System/integration/administrator/components/com_foos/MenuItem.cy.js。このコードは複雑で、多くの特別な機能を示しています。

まず、メニュー項目のすべての関連プロパティを設定する定数を定義しました。これには、関連プロパティを変更する場合に 1 か所だけを調整すればよいという利点があります。

const testMenuItem = {
  'title': 'Test MenuItem',
  'menuitemtype_title': 'COM_FOOS',
  'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
}

次に、ファイルのコード全体が表示されますMenuItem.cy.js

// tests/System/integration/administrator/components/com_foos/MenuItem.cy.js

describe('Test menu item', () => {
  beforeEach(() => {
    cy.doAdministratorLogin(Cypress.env('username'), Cypress.env('password'))
  })

  it('creates a new menu item', function () {
    const testMenuItem = {
      'title': 'Test MenuItem',
      'menuitemtype_title': 'COM_FOOS',
      'menuitemtype_entry': 'COM_FOOS_FOO_VIEW_DEFAULT_TITLE'
    }

    cy.visit('administrator/index.php?option=com_menus&view=item&client_id=0&menutype=mainmenu&layout=edit')
    cy.checkForPhpNoticesOrWarnings()
    cy.get('h1.page-title').should('contain', 'Menus: New Item')

    cy.get('#jform_title').clear().type(testMenuItem.title)

    cy.contains('Select').click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_title).click()
    cy.get('.iframe').iframe('#collapse1-heading').contains(testMenuItem.menuitemtype_entry).click()

    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_list')
    cy.clickToolbarButton('Save & Close')
    cy.wait('@item_list')
    cy.get('#system-message-container').contains('Menu item saved.').should('exist')

    // Frontend
    cy.visit('index.php')
    cy.get('.sidebar-right').contains(testMenuItem.title).click()
    cy.get('[data-test="foo-main"]').should('contain.text', 'Hello Foos')
    cy.checkForPhpNoticesOrWarnings()

    // Trash
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.clickToolbarButton('Action')
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_trash')
    cy.clickToolbarButton('trash')
    cy.wait('@item_trash')
    cy.get('#system-message-container').contains('Menu item trashed.').should('exist')

    // Delete
    cy.visit('administrator/index.php?option=com_menus&view=items&menutype=mainmenu')
    cy.setFilter('published', 'Trashed')
    cy.searchForItem(testMenuItem.title)
    cy.checkAllResults()
    cy.on("window:confirm", (s) => {
      return true;
    });
    cy.intercept('index.php?option=com_menus&view=items&menutype=mainmenu').as('item_delete')
    cy.clickToolbarButton('empty trash');
    cy.wait('@item_delete')
    cy.get('#system-message-container').contains('Menu item deleted.').should('exist')
  })
})
  • このコードでは、何かをテストしてからすべてを削除し、初期状態を復元する例を見ることができます。この方法で、必要なだけ何度でもテストを繰り返すことができます。初期状態を復元しないと、2 回目のテスト実行は失敗します。 2 つの類似した要素を格納することはできません。

注: テストは次のようにする必要があります。

  • 再現可能。
  • 具体的に言うと、これは、限定された問題をテストする必要があり、そのためのコードが大規模になりすぎないようにすることを意味します。
  • 他のテストから独立しています。
  • また、 [^docs.cypress.io/api/commands/intercept] で定義されたインターセプトされたルートをcy.intercept()エイリアスとして使用し、 でエイリアスとして定義されたルートを待機する方法を確認できますcy.wait()

cy.wait(2000);このようなアプリケーションのテストを作成する場合、コマンドなどでランダムな値を使用したくなりますcy.wait。このアプローチの問題は、これが開発ではうまく機能する可能性があることですが、常に機能することが保証されていないことです。なぜでしょうか?基盤となるシステムは予測が難しいものに依存しているため、何を待っているのかを正確に定義することをお勧めします。

cy.on("window:confirm", (s) => {
  return true;
});
  • 最後になりましたが、テスト コードには、Cypress のビルドインと、拡張機能開発者が再利用できる Joomla の典型的な関数が含まれています。たとえば、 または は、cy.setFilter('published', 'Trashed')個々cy.clickToolbarButton('Save & Close')のテストのソリューションが一般的に見つかり、特に Joomla 開発者が頻繁に必要とする関数です。 。
非同期コードと同期コードの混合

Cypress コマンドは非同期です。つまり、値を返しません。Cypressgenerateを起動すると、コマンドはすぐには実行されませんが、コマンドをシリアルに読み取ってキューに入れます。テストで非同期コードと同期コードを混在させると、予期しない結果が得られる可能性があります。次のコードを実行すると、予想に反してエラーが発生します。確かに、mainText = $main.text()値が変更されることも予想されていたでしょう。mainTextしかし、mainText === 'Initial'最後にはまだ有効です。なぜですか? Cypress は、最初に同期コードを実行します。つまり、変数が初期化され、その直後にthen()変数がmainText変更されているかどうかがチェックされますが、もちろんそうではありません。

let mainText = 'Initial';
cy.visit('administrator/index.php?option=com_foos&view=foos&layout=emptystate')
cy.get("main").then(
  ($main) => (mainText = $main.text())
);

if (mainText === 'Initial') {
  throw new Error(`Der Text hat sich nicht geändert. Er lautet: ${mainText}`);
}

ブラウザのコンソールで次のコードの実行を観察すると、キューの処理が非常に明確かつ視覚的にわかります。コード行は表示されていますが、要素のコンテンツが表示されるずっと前に「Cypress Test.」というテキストが表示されますmain。別の順序で。

cy.get('main').then(function(e){
  console.log(e.text())
})
console.log('Cypress Test.')
スタブとスパイ

A はstub、テストが依存する関数の動作をシミュレートする方法です。実際の関数を呼び出す代わりに、スタブはその関数を置き換えて、事前定義されたオブジェクトを返します。通常は単体テストで使用されますが、終了テストにも使用できます-エンドツーエンドのテスト。

Aspyは に似ていますstubが、まったく同じではありません。関数の動作は変更しませんが、そのままにしておきます。関数がどのように呼び出されるかに関する情報を取得します。たとえば、関数が呼び出されているかどうかを確認します。正しいパラメータを使用するか、関数が呼び出される頻度をカウントします。

次の例は、 aspyと astubの動作を示しています。 を介して要素const stub = cy.stub()を作成しstub、次のステップでfalse最初の呼び出しとtrue2 番目の呼び出しに対して返されるものを決定します。 を使用すると、 がに使用されるようcy.on('window:confirm', stub)になります。 次のステップでは、 要素を使用して作成します, の呼び出しを観察します。ここで、最初の呼び出しでカテゴリーの削除が拒否され、2 回目の呼び出しでそれが確認されることをテストします。そうすることで、 は戻り値がどのようになるかを確実に期待できることを保証します。は、関数が実際に呼び出されたこと、および呼び出された頻度を確認するのに役立ちます。stubwindow:confirm'cy.spy(win, 'confirm').as('winConfirmSpy')Spy'window:confirm'stub'window:confirm'@winConfirmSpy

// tests/System/integration/administrator/components/com_foos/FoosList.cy.js
...
const stub = cy.stub()

stub.onFirstCall().returns(false)
stub.onSecondCall().returns(true)

cy.on('window:confirm', stub)

cy.window().then(win => {
  cy.spy(win, 'confirm').as('winConfirmSpy')
})

cy.intercept('index.php?option=com_categories&view=categories&extension=com_foos').as('cat_delete')
cy.clickToolbarButton('empty trash');

cy.get('@winConfirmSpy').should('be.calledOnce')
cy.get('main').should('contain.text', testFoo.category)


cy.clickToolbarButton('empty trash');
cy.wait('@cat_delete')

cy.get('@winConfirmSpy').should('be.calledTwice')

cy.get('#system-message-container').contains('Category deleted.').should('exist')
...

呼び出しに固定値を設定するだけの場合は'window:confirm'、次のコードで十分です。

cy.on("window:confirm", (s) => {
  return true;
});

結論

この記事では、Cypress を使用した E2E テストの基本理論と実践的な機能について説明しました。私は Joomla インストールを使用して、Web サイト上の Joomla コンポーネントが期待どおりに動作することを確認するさまざまなテストの作成方法を示しました。また、Cypress をカスタマイズする方法も示しましたcypress.json ファイルのテスト ランナーと、カスタマイズされた Cypress コマンドの使用方法。これは、わかりやすい例を使用して行われました。

Joomla を例として使用した Cypress のツアーを楽しんでいただき、多くの知識とインスピレーションを得ることができたことを願っています。

An online collaborative community manual for Joomla! users, developers or anyone interested in learning more about Joomla! Currently, we have more than 9,000 articles written, maintained, and translated by our Joomla! community members. 

このウェブサイトは、さまざまな言語への翻訳を支援するために自動翻訳システムを使用していることにご注意ください。さまざまなテキストに表示される可能性のあるエラーやタイプミスについてはご容赦ください。