【minitest】input要素の文字列はvalue属性の値を検証しなければならない
結論
input要素で表示されている文字列の検証はvalue属性の値を検証する必要がある。
- input要素のvalue属性の値の検証は、
[value='値']で検証対象を取得できる - textarea要素の文字列の検証は、
text: 'テキスト'でタグで囲まれた文字列を検出できる
input要素とtextarea要素のテキストの表示方法の違い
- input要素: value属性で表示テキストの値を持つ
- textarea要素: 開始タグと終了タグで囲まれたテキストが表示される
具体例
例) 以下のフォーム内に表示されている文字列の表示テストを行う
<form action="..." accept-charset="UTF-8" method="post">> <div> <input type="text" value="インプットの文字列" name="..." id="input"> <textarea name="..." id="textarea">テキストエリアの文字列</textarea> </div> <div> <input type="submit" name="commit" value="更新する"> </div> </form>
失敗するテスト
input要素の文字列はassert_text では検出できない(input内の文字列はテキストでは無いため)。
class DisplayTest < ApplicationSystemTestCase test 'インプットの文字列が表示される' do # テスト対象の文字列を取得できないためエラー assert_text "インプットの文字列" # パスする assert_text "テキストエリアの文字列" end end
成功するテスト
input要素のvalueの値を検証することで文字列を検出できる。
class DisplayTest < ApplicationSystemTestCase test '' do # input idを持つインプット要素のvalue要素の値を検証 assert_selector "input#input[value='インプットの文字列']" assert_text "テキストエリアの文字列" end end
参照
感想
input要素の文字列が検出できずにハマった... HTMLもテストも奥が深い。
Turboを使ったRailsのシステムテストではJavaScriptの処理完了のタイミングに注意
test-unitでのシステムテストでちょっとハマったのでメモ。
症状
テスト実行時にログイン処理が不安定(ログインできる/できない)という症状が発生。
class BooksTest < ApplicationSystemTestCase setup do @book = books(:one) # ログイン処理 visit root_url fill_in 'Eメール', with: 'alice@example.com' fill_in 'パスワード', with: 'password' click_on 'ログイン' end test 'visiting the index' do visit books_url assert_selector 'h1', text: 'Books' # ここでテストがパスしない end end
原因
Railsアプリが描画処理にTurboを使用しており、JavaScriptの描画処理(画面遷移)の完了を待たずにCapybaraが動作してしまうため。
- テスト検証のタイミングに描画処理の完了が間に合っていない(テストのフライング)
- Turboに限らず、Ajaxでの非同期処理でも発生するらしい
- ブラウザによってはパスすることもあるっぽい
対策
assert_xxx 系のメソッドでJavaScriptの描画処理の完了を待たせる。
assert_text、assert_selector等が使える- デフォルトで最大2秒待ってくれる
class BooksTest < ApplicationSystemTestCase setup do @book = books(:one) # ログイン処理 visit root_url fill_in 'Eメール', with: 'alice@example.com' fill_in 'パスワード', with: 'password' click_on 'ログイン' assert_text 'ログインしました。' # JavaScriptの処理完了を待たせる end test 'visiting the index' do visit books_url assert_selector 'h1', text: 'Books' # JavaScriptの処理完了後に実行される end end
その他の対策
sleepを使って処理を待たせる- テスト実行が遅くなるので微妙かも...
- Turboの無効化
- アプリ自体の描画処理が遅くなるっぽい
- React、Vue等では対策できない
感想
久々に時間溶かしてしまった... システムテストは描画処理の完了と検証開始のタイミングは必ずしも一致していないことに注意していこう。
Chromeでフルスクリーンのスクリーンショット(キャプチャ)を撮影する
Chrome DevToolsを使用してフルスクリーンのスクリーンショット(キャプチャ)を撮影する方法。 この方法で縦スクロールのページも撮影できる。
手順
- DevTools起動
- レスポンシブ設定を
Responsiveに設定 - 右上の3点リーダーの
Capture full size Screenshotで撮影 - ダウンロードフォルダに保存される
1. DevTools起動
以下のショートカットでDevToolsを起動。
2. レスポンシブ設定をResponsiveに設定
DevTools内のレスポンシブ設定ボタンを押下後、レスポンシブ設定をResponsive に設定。

3. 右上の3点リーダーのCapture full size screenshotで撮影
DevTools内の右上の3点リーダーをクリックすると表示されるCapture full size screenshot をクリックすると撮影される。

4. ダウンロードフォルダに保存される
ダウンロードフォルダにpng で保存される。
test-unitについて
test-unitの基礎知識。
test-unitとは?
単体テスト
- 単一のプログラム部品(モジュール)を対象としたテスト
- クラス、メソッド、関数などの最小単位が対象
- 問題の早期発見が目的
- モジュール単位の動作の保証する
単体テストツール
ドライバ
- driver
- 最小単位の処理(関数等)を呼び出すツール
スタブ
- stub
- 最小単位の処理(関数等)のプログラムのダミー
test-unitの使い方
Test::Unit::TestCaseを継承したテスト用クラスを用意するtest_xxxという名前でテスト用メソッドを用意する- ファイルを実行する
例) モデルUser のメソッドgreeting がHello! を返すことを確認するテスト
下記の内容でファイルを作成する。
# user.rb require 'test/unit' class User def self.greeting 'Hello!' end end class TestUser < Test::Unit::TestCase def test_greeting assert_equal 'Hello!', User.greeting end end
メソッドの動作をテストする。
# テストが成功 $ ruby user.rb Loaded suite test Started . Finished in 0.000289 seconds. ------------------------------------------------------------------------------------- 1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed ------------------------------------------------------------------------------------- 3460.21 tests/s, 3460.21 assertions/s
# テストが失敗 # 大文字Hが小文字hになっている $ ruby test.rb Loaded suite test Started F ===================================================================================== Failure: test_greeting(TestUser) test.rb:11:in `test_greeting' 8: 9: class TestUser < Test::Unit::TestCase 10: def test_greeting => 11: assert_equal 'Hello!', User.greeting 12: end 13: end <"Hello!"> expected but was <"hello!"> diff: ? Hello! ? h ? ? ===================================================================================== Finished in 0.004249 seconds. ------------------------------------------------------------------------------------- 1 tests, 1 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 0% passed ------------------------------------------------------------------------------------- 235.35 tests/s, 235.35 assertions/s
test-unitの便利メソッド
starup- テスト郡実行前に処理を実行する
shutdown- テスト郡実行後に処理を実行する
setup- 各テストの実行前に処理を実行する
cleanup- テストが
passedの場合にテスト実行後に処理を実行する
- テストが
teardown- 各テストの実行後に処理を実行する
モックオブジェクトとは?
- テスト対象の処理を実行せず、引数の検証とテストを動かす最低限の出力を行う
- Rubyでモックオブジェクトを作るライブラリは複数存在する
- FlexMock
- MiniTest::Mock
- RR
- WebMock
参照
配列同士の比較はSetを使うと順序の考慮が不要になる
バリデーションの実装で配列同士の比較で要素の順序を考慮せず比較する方法としてSetを教えてもらいました。
結論
Rubyでは集合を表すSetを使うことで配列の順序の考慮が不要になる。
class Set (Ruby 3.2 リファレンスマニュアル)
Set(集合)とは?
RubyのSetは一意な値の集合を表現するためのデータ構造です。 配列やハッシュと同じオブジェクトの集合を表すものですが以下のような特徴があります。
- 要素が重複しない
- 要素の順序は保持されない
配列、ハッシュとの違い
配列との違い
- 重複要素の扱い
- 配列: 複数持てる
- 集合: 一意な要素のみ
- 順序の扱い
- 配列: 保持する
- 集合: 保持しない
- 重複要素の扱い
ハッシュとの違い
- 要素の組み合わせ
- ハッシュ: キーと値の組み合わせ
- 集合: 単一の要素の集合
- 一意性の保証
- ハッシュ: キーが一意である必要がある
- 集合: 要素自体が一意である必要がある
- 要素の組み合わせ
ざっくりですが集合は「そのオブジェクトの集まりが順序を考慮せず、一意性を保証したい」といった場合に活用できそうです。
コンソールで動かしてみる
集合のオブジェクトはSet.new(集合の初期要素) で作成できる。
集合の初期要素としては以下が該当する。
- 空
- 配列
- ハッシュ
たぶん他にもあるっぽい。
library set (Ruby 3.2 リファレンスマニュアル)
require 'set' Set.new() => #<Set: {}> # 空の集合 Set.new([1,2]) => #<Set: {1, 2}> # 配列を元にした集合 Set.new({a: 1, b: 2}) => #<Set: {[:a, 1], [:b, 2]}> # ハッシュを元にした集合
例) 配列は順序を保持するが、集合は保持しない
# 配列 array_1 = [1,2] => [1, 2] array_2 = [2,1] => [2, 1] array_1 == array_2 => false # 要素の順序を保持されるのでfalse # 集合 require 'set' set_1 = Set.new([1,2]) => #<Set: {1, 2}> set_2 = Set.new([2,1]) => #<Set: {2, 1}> set_1 == set_2 => true # 要素の順序が保持されないのでtrue
感想
Setの存在すら知らなかったので良い機会になった。 Rubyは便利だなーと改めて感じた。
参照
class Set (Ruby 3.2 リファレンスマニュアル) library set (Ruby 3.2 リファレンスマニュアル)