Closed3

Cypress の Core Concepts を読んでみた

なぜ、Cypress を学ぶのか?

Introdyction to Cypress

  • Cypress によるDOM操作
  • サブジェクトとコマンド・チェーン操作
  • アサーションはなにをするのか
  • タイムアウトを使用

シンプルである

describe('Post Resource', () => {
  it('Creating a New Post', () => {
    cy.visit('/posts/new')     // Visit the page at /posts/new.

    cy.get('input.post-title') // Find the <input> with class post-title.
      .type('My First Post')   // Type "My First Post" into it.

    cy.get('input.post-body') // Find the <input> with class post-body.
      .type('Hello, world!')       // Type "Hello, world!" into it.

    cy.contains('Submit')           // Find the element containing the text Submit.
      .click()                              // Click it.

    cy.url() // Grab the browser URL, ensure it includes /posts/my-first-post.
      .should('include', '/posts/my-first-post')

    cy.get('h1') // Find the h1 tag, ensure it contains the text "My First Post".
      .should('contain', 'My First Post')
  })
})

Elements をクエリする

jQueryの強力なセレクタエンジンを活用し、現代のWeb開発者がテストを身近で読みやすいものにするための支援を行っています。

要素を選択するためのベストプラクティスに興味がありますか

すべての DOM クエリを、実際の Web アプリケーションの動作により適した堅牢なリトライおよびタイムアウトロジックでラップします。私たちは、DOM 要素の検索方法の小さな変更と引き換えに、すべてのテストの安定性を大きく向上させました。flake を永久に追放する

Cypressでは、DOM要素と直接対話したい場合、要素を第1引数として受け取るコールバック関数で.then()を呼び出します。リトライとタイムアウトの機能を完全にスキップして、従来の同期的な作業を行いたい場合は、Cypress.$.Then()を使用

テキストコンテンツをクエリする

cy.contains()コマンドを使う

// Find an element in the document containing the text 'New Post'
cy.contains('New Post')

CypressはWebアプリケーションの非同期性を予測しており、最初に要素が見つからなかったときにすぐに失敗することはありません。その代わり、Cypressはアプリが何をしていても、それを終了させるための時間的余裕を与えます。

これはタイムアウトと呼ばれ、ほとんどのコマンドは特定のタイムアウト時間をカスタマイズすることができます(デフォルトのタイムアウトは4秒です)。これらのコマンドは、APIドキュメントにタイムアウトオプションが記載されており、要素を見つけるために何ミリ秒継続するかを設定する方法が詳しく説明されています。



// Find an element within '.main' containing the text 'New Post'
cy.get('.main').contains('New Post')
// Give this element 10 seconds to appear
cy.get('.my-slow-selector', { timeout: 10000 })

コアコンセプト

Webアプリケーションの動作に合わせるため、Cypressは非同期で、アプリが期待される状態になるのを待つのをいつ止めるかを知るためにタイムアウトに依存しています。タイムアウトは、グローバルに設定することも、コマンド単位で設定することもできます。

タイムアウトとパフォーマンス

タイムアウト時間を長くすると、テストが失敗するまでの時間が長くなるため、パフォーマンスとのトレードオフになります。コマンドは常に、期待される条件を満たすとすぐに実行されます。したがって、作業中のテストはアプリケーションが許す限り高速に実行されます。タイムアウトで失敗するテストは、設計上タイムアウト時間を丸々消費します。つまり、アプリの特定の部分に合わせてタイムアウト時間を長くすることはできても、 「念のために余分に長くしておく」ことはできないということです。

Chains of Commands
コマンドをchainさせるために使うメカニズムを理解することは重要。

Cypressはあなたに代わってPromiseのchainsを管理し、各コマンドは次のコマンドに「subject」をもたらし、連鎖が終わるかエラーが発生するまで管理します。開発者はPromiseを直接使う必要はありませんが、その仕組みを理解することは役に立ちます!

インタラクション
cy.get()cy.contains()コマンド.click().type()を使ってページ上の要素をクリックしたり入力する

  • .blur() - フォーカスされた要素を blur
  • .focus() - DOM要素を focus
  • .clear() : textarea要素input要素 の値をclear
  • .check() : checkbox要素radio要素check
  • .uncheck() : checkbox要素uncheck.
  • .select(): <select>要素内の<option>要素select
  • .dblclick() : DOM要素を Double-click
  • .rightclick() - Right-click a DOM element

アサーション

アサーションは、ある要素が可視であること、特定の属性、CSSクラス、または状態を持つことを保証するようなことを可能にします。アサーションは、アプリケーションの望ましい状態を記述できるようにするコマンドです。Cypress は、要素がこの状態になるまで自動的に待機し、アサーションがパスしない場合はテストに失敗します。ここでは、アサーションの動作について簡単に説明。

cy.get(':checkbox').should('be.disabled')

cy.get('form').should('have.class', 'form-horizontal')

cy.get('input').should('not.have.value', 'US')

新しいCypressチェーンは常にcy.[command]で始まり、コマンドによって得られるものが、次に呼び出せる他のコマンドを確立します(chained)。

cy.clearCookies()のように、nullを返すので連鎖できないメソッドもあります。

cy.get()やcy.contains()のようないくつかのメソッドはDOM要素をもたらし、.click()やcy.contains()のように、さらなるコマンドをその上に連結することを可能にします(それらがDOM主体を期待すると仮定して)。

いくつかのコマンドは、...から連鎖することができます。
cy.clearCookies()のようにサブジェクトを操作しないもの。
DOM要素のような)特定の種類のサブジェクトを生成するコマンド: .type().
cyとサブジェクトを生成するコマンドの両方: cy.contains()。
いくつかのコマンドは...
null、つまり、そのコマンドの後に連鎖するコマンドはない: cy.clearCookie().
.click()は、元々生成されたものと同じサブジェクトを生成します。
.wait()に対応する新しいサブジェクト。

cy.clearCookies() // Done: 'null' was yielded, no chaining possible

cy.get('.main-container') // Yields an array of matching DOM elements
  .contains('Headlines') // Yields the first DOM element containing content
  .click() // Yields same DOM element from previous command

サイプレスのコマンドは、主語を返すのではなく、主語を降ろすのです。覚えておいてください。Cypressのコマンドは非同期で、後日実行するためにキューに入れられる。実行中、subjectはコマンドから次のコマンドに渡され、各コマンドの間に多くの有用なCypressコードが実行され、すべてが順番に実行されることを確認します。

要素を参照する必要性を回避するために、サイプレスにはエイリアシングと呼ばれる機能があります。エイリアシングは、将来使用するために要素の参照を保存し、保存するのに役立ちます

コマンドフローに飛び込んで、直接手を動かしたい?問題ありません。コマンドチェーンに .then() を追加してください。前のコマンドが解決されると、最初の引数としてyielded subjectを持つコールバック関数が呼び出されます。

.then()の後もコマンドを連鎖させたい場合は、それらのコマンドに渡したいサブジェクトを指定する必要があります。Cypressはあなたのためにそれを次のコマンドに渡します。

cy
  // Find the el with id 'some-link'
  .get('#some-link')

  .then(($myElement) => {
    // ...massage the subject with some arbitrary code

    // grab its href property
    const href = $myElement.prop('href')

    // strip out the 'hash' character and everything after it
    return href.replace(/(#.*)/, '')
  })
  .then((href) => {
    // href is now the new subject
    // which we can work with now
  })
cy.get('.my-selector')
  .as('myElement') // sets the alias
  .click()

/* many more actions */

cy.get('@myElement') // re-queries the DOM as before (only if necessary)
  .click()

非同期

Cypressのコマンドは、呼び出された瞬間には何もせず、後で実行されるように自分自身をエンキューするということを理解することは非常に重要なことです。Cypressのコマンドは非同期であるというのは、このことを指しています。

it('changes the URL when "awesome" is clicked', () => {
  cy.visit('/my/resource/path') // Nothing happens yet

  cy.get('.awesome-selector') // Still nothing happening
    .click() // Nope, nothing

  cy.url() // Nothing to see, yet
    .should('include', '/my/resource/path#awesomeness') // Nada.
})

// Ok, the test function has finished executing...
// We've queued all of these commands and now
// Cypress will begin running them in order!

Cypressは、テスト関数が終了するまで、ブラウザ自動化マジックをキックオフしない。

非同期と同期のコードを混在させる
Cypressコマンドと同期コードを混在させようとする場合、Cypressコマンドが非同期で実行されることを覚えておくことが重要です。同期コードはすぐに実行され、その上のCypressコマンドの実行を待つことはありません。

上記のコードを書き換えて、コマンドが期待通りに実行されるようにするための一つの方法

it('does not work as we expect', () => {
  cy.visit('/my/resource/path') // Nothing happens yet

  cy.get('.awesome-selector') // Still nothing happening
    .click() // Nope, nothing
    .then(() => {
      // placing this code inside the .then() ensures
      // it runs after the cypress commands 'execute'
      let el = Cypress.$('.new-el') // evaluates after .then()

      if (el.length) {
        cy.get('.another-selector')
      } else {
        cy.get('.optional-selector')
      }
    })
})
it('test', () => {
  let username = undefined // evaluates immediately as undefined

  cy.visit('https://app.com') // Nothing happens yet
  cy.get('.user-name') // Still, nothing happens yet
    .then(($el) => {
      // Nothing happens yet
      // this line evaluates after the .then() executes
      username = $el.text()

      // evaluates after the .then() executes
      // it's the correct value gotten from the $el.text()
      if (username) {
        cy.contains(username).click()
      } else {
        cy.get('My Profile').click()
      }
    })
})

コアコンセプト

Cypressの各コマンド(およびコマンドのチェーン)は、後で実行されるコマンドのキューに追加されただけで、すぐに戻ってきます。

コマンドからの戻り値で何か役に立つことをすることは意図的にできません。コマンドは完全に舞台裏でキューに入れられ、管理されています。

このようにAPIを設計したのは、DOMが非常に変異しやすいオブジェクトであり、常に古くなってしまうからです。Cypressは、Flakeを防ぎ、いつ処理を続行すべきかを知るために、高度に制御された決定論的方法でコマンドを管理します。

テストを続ける必要があるかどうかを決定する前に、いくつかのコマンドを実行する機会を与える必要があります。したがって、正しいテストは再帰を使用することになります。

it('changes the URL when "awesome" is clicked', () => {
  cy.visit('/my/resource/path') // Visit a URL

  cy.get('.awesome-selector') // Find an element by its selector
    .click() // Perform a click action on that element.

  cy.url() // Grab the URL
    .should('include', '/my/resource/path#awesomeness') // Assert the URL to include a specific string
})

コアコンセプト

あるステップが成功したことを確認するために必要な待機または再試行は、次のステップが始まる前に完了しなければならない。タイムアウトに達する前にそれらが正常に完了しない場合、テストは失敗します。

このスクラップは4ヶ月前にクローズされました
ログインするとコメントできます