SPA開発 7. Userモデル開発 #06
2019年11月17日に公開

【Rails】ユーザーモデルのバリデーションをテストする

今回達成すること

ユーザーモデルで設定したバリデーションが正しく機能しているか、Railsのテスト機能を使って検証していきます。

今回テストする項目

ユーザーモデルのテスト項目は以下のとおりです。

email保存状態のテスト

  • emailが小文字で保存されているか

emailバリデーションテスト

  • 空の場合エラーになっているか(入力必須)
  • 256文字以上の場合エラーになっているか
  • 不正な書式にエラーを出しているか

email一意性テスト

  • 同じemailのユーザーは何人でも保存できているか
  • 同じemailでかつ、アクティブなユーザーが既に存在しているときにエラーを出しているか

passwordバリデーションテスト

  • 空の場合エラーになっているか(入力必須)
  • 8文字以下のパスワードにエラーを出しているか
  • 72文字を超える場合はエラーを出しているか
  • 不正な書式はエラーになっているか

この記事の読み方

最終的なuser_test.rbは一番下に記載しています。

最終的なuser_test.rb

以下、全てuser_test.rbを編集していきます。

編集の都度Railsのテストコマンドを実行し、テストが通ることを確認した上で次に進むことをおすすめします。

myapp $ rais t #もしくは $ rails test

それでは実装に入りましょう。

email保存状態のテスト

emailが小文字で保存されているか?

test/models/user_test.rb
test "email_downcase" do
  # emailが小文字で保存されているか
  email = "USER@EXAMPLE.COM"
  user = User.create(email: email, password: "password")
  assert user.email == email.downcase
end
  • assert

    引数に渡した式がtrueであるか判定します。

emailバリデーションテスト

空の場合にエラーになっているか?(入力必須)

test/models/user_test.rb
test "email_validator" do
  user = User.new(password: "password")
  # 入力必須
  user.save
  required = ["メールアドレスを入力してください"]
  assert_equal(required, user.errors.full_messages)
end
  • assert_equal(期待値, 実際値)

    期待値と実際値が等しいかを判定します。

    この場合、用意されたエラーメッセージと実際のエラーメッセージが等しいかを判定しています。

文字数が256文字以上の場合エラーになっているか

test/models/user_test.rb
test "email_validator" do
	...

  # OK 255文字以下
  ok_email = "#{"a" * (255 - 12)}@example.com"
  user.email = ok_email
  assert user.save

  # NG 255文字超
  user.email = ok_email + "a"
  user.save
  length = ["メールアドレスは255文字以内で入力してください"]
  assert_equal(length, user.errors.full_messages)
end

不正な書式にエラーを出しているか

  1. ハイフンとアンダーバー、プラスの記号が混ざったemailはバリデーションを通っているか?
  2. その他の記号、大文字、@が抜けている場合はエラーとなっているか?
test/models/user_test.rb
test "email_validator" do
	...

  # OK 書式チェック
  # /\A\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\z/
  oks = %w(
    A@EX.COM a-_@e-x.c-o_m.j_p a.a@ex.com a@e.co.js 1.1@ex.com a.a+a@ex.com
  )
  oks.each do |ok|
    user.email = ok
    assert user.save
  end

  # NG 書式チェック
  ngs = %w(
    aaa a.ex.com メール@ex.com a~a@ex.com a@|.com a@ex./ .a@ex.com 
    a@ex.com A@ex.com a@?,com 1@ex.com "a"@ex.com a@ex@co.jp
  )
  format = ["メールアドレスの書式が正しくありません"]
  ngs.each do |ng|
    user.email = ng
    user.save
    assert_equal(format, user.errors.full_messages)
  end
end

email一意性テスト

同じemailのユーザーは何人でも保存できているか?

test/models/user_test.rb
test "email_uniqueness" do
  # アクティブなユーザーがいない場合に同じユーザーは保存できているか
  10.times do |n|
    user = User.new(email: "user@example.com", password: "passowrd")
    assert user.save
  end
end

アクティブなユーザーが既に存在しているときにエラーを出しているか

  1. アクティブプラグがtrueのユーザーがいるときは、同じemailでは保存できないようになっているか?
  2. また、そのemailが大文字の場合でもエラーを吐いているか?
  3. アクティブなユーザーが存在しなくなったら、同じemailで保存できているか?
test/models/user_test.rb
test "email_uniqueness" do
  ...

  assert @user.activated
  user = @user.dup

  # NG アクティブなユーザーが既に存在している場合
  user.save
  unique = ["メールアドレスはすでに存在します"]
  assert_equal(unique, user.errors.full_messages)

  # NG emailが大文字でもエラーを出しているか
  user.email = @user.email.upcase
  user.save
  assert_equal(unique, user.errors.full_messages)
  
  # OK アクティブなユーザーがいなくなった場合
  @user.update(activated: false)
  assert user.save
end
  • user = @user.dup

dup はオブジェクトのコピーを作成するRubyのメソッドです。

dup は保存前の値だけをコピーするのに対して、 clone は、idやcreated_at(作成時間)までコピーします。

  • dup でコピーしたユーザーオブジェクト

    2019-11-17 14-00-37

  • cloneでコピーしたユーザーオブジェクト

    2019-11-17 14-00-38

passwordバリデーションテスト

空の場合エラーになっているか?(入力必須)

test/models/user_test.rb
test "password_validator" do
  user = User.new(email: "user@example.com")

  # 入力必須
  user.save
  required = ["パスワードを入力してください"]
  assert_equal(required, user.errors.full_messages)
end

8文字以下のパスワードにエラーを出しているか?

test/models/user_test.rb
test "password_validator" do
  ...
    
  # NG 8文字未満
  user.password = "a" * 7
  user.save
  min_length = ["パスワードは8文字以上で入力してください"]
  assert_equal(min_length, user.errors.full_messages)
end

72文字を超える場合はエラーを出しているか?

test/models/user_test.rb
test "password_validator" do
  ...
    
  # NG 72文字超
  user.password = "a" * 73
  user.save
  max_length = ["パスワードは72文字以内で入力してください"]
  assert_equal(max_length, user.errors.full_messages)

  # ok 72文字以下
  user.password = "a" * 72
  assert user.save
end

不正な書式はエラーになっているか

test/models/user_test.rb
test "password_validator" do
  ...
    
  # NG 書式チェック
  # VALID_PASSWORD_REGEX = /\A[\w\-]+\z/
  ngs = %w(pass/word pass.word |~=?+"a" 12345678 ABCDEFGH)
  format = ["パスワードは半角英数字、ハイフン、アンダーバーが使えます"]
  ngs.each do |ng|
    user.password = ng
    user.save
    assert_equal(format, user.errors.full_messages)
  end
end

全ての編集が完了したらテストコマンドを実行してエラーが出なければOKです。

myapp $ rails t
...
5 tests, 47 assertions, 0 failures, 0 errors, 0 skips

本番環境にpushする

ブランチを作成している場合はmasterブランチにマージします。

myapp $ git commit -am "user_validate_test" 
myapp $ git checkout master
myapp $ git merge user_modeling
myapp $ git push
myapp $ git push heroku

まとめ

これにてユーザーモデル開発は終了です。

今回のユーザーモデル開発は、

  1. マイグレーションファイルの作成
  2. $ rails db:migrate を実行してテーブルの作成
  3. バリデーションの設定
  4. seedデータの投入
  5. バリデーションテスト

の流れで一連の作業を行いました。(4と5は逆でもOK)

今後もこの流れを覚えておくと良きです。

さて、次回は?

さて、次回は。。。

今悩んでいるのですが、Railsチュートリアルに従ってユーザーログイン機能の実装をしましょうか。

はたまた、モデルを一通り作ってしまうか。。。

ログイン機能の場合、JWT認証を使って実装していきます。

更新をご期待ください。(歯切れが悪くてすみません。)

最終的なuser_test.rb

test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    init
  end

  test "sqlite3_test" do
    assert_not_nil @user
  end

  test "email_downcase" do
    # emailが小文字で保存されているか
    email = "USER@EXAMPLE.COM"
    user = User.create(email: email, password: "password")
    assert user.email == email.downcase
  end

  test "email_validator" do
    user = User.new(password: "password")
    # 入力必須
    user.save
    required = ["メールアドレスを入力してください"]
    assert_equal(required, user.errors.full_messages)

    # OK 255文字以下
    ok_email = "#{"a" * (255 - 12)}@example.com"
    user.email = ok_email
    assert user.save

    # NG 255文字超
    user.email = ok_email + "a"
    user.save
    length = ["メールアドレスは255文字以内で入力してください"]
    assert_equal(length, user.errors.full_messages)

    # OK 書式チェック
    # /\A\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\z/
    oks = %w(
      A@EX.COM a-_@e-x.c-o_m.j_p a.a@ex.com a@e.co.js 1.1@ex.com a.a+a@ex.com
    )
    oks.each do |ok|
      user.email = ok
      assert user.save
    end

    # NG 書式チェック
    ngs = %w(
      aaa a.ex.com メール@ex.com a~a@ex.com a@|.com a@ex./ .a@ex.com 
      a@ex.com A@ex.com a@?,com 1@ex.com "a"@ex.com a@ex@co.jp
    )
    format = ["メールアドレスの書式が正しくありません"]
    ngs.each do |ng|
      user.email = ng
      user.save
      assert_equal(format, user.errors.full_messages)
    end
  end

  test "email_uniqueness" do
    # アクティブなユーザーがいない場合に同じユーザーは保存できているか
    10.times do |n|
      user = User.new(email: "user@example.com", password: "passowrd")
      assert user.save
    end

    assert @user.activated
    user = @user.dup

    # NG アクティブなユーザーが既に存在している場合
    user.save
    unique = ["メールアドレスはすでに存在します"]
    assert_equal(unique, user.errors.full_messages)

    # NG emailが大文字でもエラーを出しているか
    user.email = @user.email.upcase
    user.save
    assert_equal(unique, user.errors.full_messages)

    # OK アクティブなユーザーがいなくなった場合
    @user.update(activated: false)
    assert user.save
  end

  test "password_validator" do
    user = User.new(email: "user@example.com")

    # 入力必須
    user.save
    required = ["パスワードを入力してください"]
    assert_equal(required, user.errors.full_messages)

    # NG 8文字未満
    user.password = "a" * 7
    user.save
    min_length = ["パスワードは8文字以上で入力してください"]
    assert_equal(min_length, user.errors.full_messages)

    # NG 72文字超
    user.password = "a" * 73
    user.save
    max_length = ["パスワードは72文字以内で入力してください"]
    assert_equal(max_length, user.errors.full_messages)

    # ok 72文字以下
    user.password = "a" * 72
    assert user.save

    # NG 書式チェック
    # VALID_PASSWORD_REGEX = /\A[\w\-]+\z/
    ngs = %w(pass/word pass.word |~=?+"a" 12345678 ABCDEFGH)
    format = ["パスワードは半角英数字、ハイフン、アンダーバーが使えます"]
    ngs.each do |ng|
      user.password = ng
      user.save
      assert_equal(format, user.errors.full_messages)
    end
  end

end
現在、カテゴリー「Rails apiとNuxt.jsでSPA開発」のデモアプリを構築中です。記事になるまでもう少々のお時間が必要です。ブログの更新が止まって申し訳ありません。デモアプリの進捗状況は こちらの記事 で随時お伝えしてまいります。
スポンサー広告
次の記事はこちらです
SPA開発
今日のTweet
スポンサー広告