Open6

【Techouse × Leaner共同開催】Ruby / Rails勉強会

しげるしげる

UIの変更に備えるCapybaraでのフロントエンドテスト

https://twitter.com/zuckey_17

話すこと

  • Selenium + Capybara によるテスト

  • 弊社での実践例で、ベストプラクティスではない

  • 話さないこと

    • フロントエンド全般のテストの話

今日話すところ

  • E2E とintegration test の間のところ
  • Capybara で E2E テストを書く難しさ
    • ログインからテストをするところまで難しい
      • 長大なテストコードになる
    • UI の変更箇所に対して、テストの変更箇所が多くなる

→ つらい部分はあるが書かないわけには行かない

工夫しているところ

    1. テストの範囲を画面単位で特定のテストコンテキストにおいて、機能の動作を確認するテスト
    • 対象範囲を狭める
    • ログインセッションをモックしたり FactoryBot を利用して、テストしたいところにフォーカスする
    1. ページオブジェクトパターン
    • UI に必要な DOM の操作をテストコード書かずにオブジェクトにまとめる
    • DOM操作という直感的でないコードを排除して読みやすくする
テストにおけるページオブジェクトパターンについて教えて by ChatGPT

ページオブジェクトパターンは、自動テスト(特にUIテスト)の設計パターンの一つで、テストスクリプトとテスト対象のWebページとの間に抽象化レイヤーを設けることで、テストコードの可読性と保守性を向上させることを目指しています。

具体的には、各Webページ(またはページの一部)を一つの「ページオブジェクト」クラスとしてモデル化し、そのクラス内にページ特有の操作(フォームへの入力、ボタンのクリックなど)や状態の確認(特定のテキストや要素の存在確認など)をメソッドとして定義します。

これにより、テストスクリプト側ではページ特有の操作や状態確認を直接記述する代わりに、ページオブジェクトのメソッドを呼び出すことでテストを行うことができます。これにより、テストコードから具体的な操作詳細やセレクタなどの情報を隠蔽することができ、テストコードの可読性と保守性が向上します。

また、ページの構造が変わった場合でも、対応するページオブジェクトのメソッドを更新すればよいため、テストの修正・メンテナンスも容易になります。

以下に、RubyとCapybaraを使用した簡単なページオブジェクトの例を示します。

class LoginPage
  include Capybara::DSL

  def visit_page
    visit '/login'
    self
  end

  def login_as(user)
    fill_in 'Email', with: user.email
    fill_in 'Password', with: user.password
    click_button 'Log in'
  end
end

# テストスクリプト側
login_page = LoginPage.new
login_page.visit_page.login_as(user)

この例では、ログインページを表すLoginPageクラスを定義し、その中にページへの遷移とログイン操作をメソッドとして定義しています。テストスクリプト側では、これらのメソッドを呼び出すことでテストを行っています。

スコープ外の話

  • Storybook のテスト / Vue のテストも使っている
  • E2E は QA チームと一緒にする

→ 適切な技術を適切なところへ

しげるしげる

SPAでフロントエンドと連携するための Rails の構成と工夫していること

https://twitter.com/phigasui

  • オフラインイベントなので、情報交換したいよ〜

  • Leaner 購買の話をする

    • rails new 2021/06
  • フロントエンドは Next

    • Rails は API モード
    • OpenAPI で Scheam 定義をしている
  • Rails Application の規模

    • rails stat
    • 1万行くらい
  • Committee で、 schema に match しているかを shared_example で書いている

  • TIps1. Typescript では camelCase / Ruby では snake_case で使いたい

    • ActionDispath::Request.parameter_parse でいい感じにしている
  • Tips2. サーバーサイドで redirect したい時にいい感じにする

API response の作り方

Presenter Class

  • model から情報を取ってきて、 Presenter を利用している
  • ActiveModel#as_json 便利

Query Class

  • 複雑な class を表現する場合
  • include ApmTraceable::Tracer
    • Datadog でトレースできるようにしている

https://speakerdeck.com/kokuyouwind/apmwotiyantoshi-outositara-itunomanikadu-zi-gemwozuo-tuteitahua

Service Class

  • Get 以外の場合で複数の Model 跨ぐ場合に、 Service class を使う

話したいこと

  • Presenter の使い回すと不要な情報が出てくる
  • Service の中に Query 系も入っている
しげるしげる

YJITの有効化で、なぜRubyが早くなるのか?調べてみた

  • YJIT 使っている人...?

    • ???「シーン」
  • Ruby3.2 / Rails 7.1 で YJIT を有効化させました!

そもそも JIT って?

  • Just-In-Time compiler

  • デメリット

    • メモリを使っちゃう

YJIT

  • shopify が開発した JIT コンパイラ
  • Ruby インタプリタと比べると YJITは早い!

Ruby って

  • Ruby は逐次実行で、推論をしている
  • JIT では型の推論を弾く
    • 現実のコードは同一の型を持ち続けることが多い
    • YJIT は型推論を省略して実行できる

Ruby 3.3

  • Ruby3.2 より早くなった
  • メモリ使用量の大幅な改善
RubyにおけるJITとYJITについて違いを交えつつ教えてください by ChatGPT

Ruby 3.0では、JIT(Just-In-Time)コンパイラが導入されています。RubyのJITコンパイラは、RubyのバイトコードをC言語のコードに変換し、それをGCCやClangなどのCコンパイラでコンパイルしてネイティブコードに変換します。これにより、一部のベンチマークでは大幅なパフォーマンス向上が見られます。

しかし、RubyのJITコンパイラは、メモリ使用量が増える、コンパイルに時間がかかる、すべてのワークロードでパフォーマンスが向上するわけではないなどの問題があります。

これに対して、YJIT(Yet Another Ruby JIT)は、Shopifyによって開発された新しいJITコンパイラで、Rubyのバイトコードを直接ネイティブコードに変換します。これにより、Cコンパイラを介さないためのオーバーヘッドを削減し、より高速なコンパイルを実現しています。

また、YJITは、メソッドのインライン化などの高度な最適化を行うことで、さまざまなワークロードでのパフォーマンス向上を目指しています。

ただし、YJITは現在開発中のものであり、すべてのRubyの機能をサポートしているわけではありません。また、YJITは現時点ではCRubyの公式リポジトリには含まれておらず、別のリポジトリで開発が進められています。

しげるしげる

普通にRailsを使っていただけなのに

  • Rails 歴 2年
    • 同じフレームワークでも書き方が全然違う
  • 普通に Rails で書いていたつもりが、痛い目にあった例を2つ紹介

ORM の使い方

  • Hotel / Reservation / Billings の model がある時

  • has_many / has_one で呼び出すことができる

  • カラムが増えるとだんだん遅くなる

    • テーブルパーティションを管理できる
    • partition key をもとに分割することができる
  • partition を使っている場合は、partition key を使わないと、クエリが遅くなる

    • どんなクエリが実行されているか意識する
データベースにおける partition について教えてください by ChatGPT

データベースにおけるパーティショニングとは、大量のデータを管理しやすくするために、一つのテーブルを複数の部分(パーティション)に分割することを指します。

パーティショニングは主に以下のような目的で行われます:

  1. パフォーマンスの向上:クエリは特定のパーティションだけを対象とすることが多いため、全体のデータ量が多くても必要なデータを素早く取得できます。これをパーティションプルーニングと呼びます。

  2. データ管理の効率化:古いデータを削除するときなど、特定のパーティションだけを対象に操作を行うことができます。

  3. 可用性の向上:一部のパーティションに障害が発生しても、他のパーティションは影響を受けません。

パーティショニングには主に以下の2つのタイプがあります:

  • 水平パーティショニング:テーブルの行を分割します。例えば、日付や地域などの特定の列の値に基づいて行を分割することがあります。

  • 垂直パーティショニング:テーブルの列を分割します。頻繁にアクセスされる列とそうでない列とを別々のテーブルに分けることで、パフォーマンスを向上させることがあります。

なお、パーティショニングはデータベースの設計や運用における高度なテクニックであり、適切な設計や運用が求められます。パーティションの設計が不適切な場合、逆にパフォーマンスが低下することもあります。

assign_attributes

  • accepts_nested_attributes_for
  • assign_attributes & accepts_nested_attributes_for が混ざるとき
    • 関連レコードが保存されることがある
    • 機能の組み合わせによって化けることがある
    • 利用するメソッドもどのように実行されるかを意識する

まとめ

  • 利用技術などの兼ね合いでアンチパターンになることがある
    • 世間一般のアンチパターンとチームのアンチパターンが異なる落とし穴がある
  • チーム特有の書き方はちゃんとまとめておこう
しげるしげる

Rails 7.1 の新機能が使いたくて調べてみた

https://twitter.com/glico800

ActiveRecord::Base.normalizes 正規化

  • 正規化のタイミング
    • assign / update するタイミング
    • ActiveRecord::FinderMethods
      • find_by / where /exists のキーワード引数には正規されている
      • キーワード引数以外は実行されない
  • 明示的に呼ぶ
    • User.normalize_attributes(:email) などで明示的に使うことができる
    • Model.normalized_variable_for

非同期クエリのサポートが拡張

  • 集計系 / 検索系 が増えている

load_async との違い

  • ActiveRecordRelation を返さない method では asyc は使えない

config/application.rb

  • async_query_executor を設定しないと使えない

用途を考える

  • データ分析で使うことができる
  • Leaner 見積もりの年間コストインパクト算出で、非同期をしたら 20 倍くらい高速化された