Railsで電話番号(固定電話・携帯電話)を正規表現で表し、バリデーションとテストを書く

はじめに

電話番号のバリデーションをするために電話番号を正規表現で表してみました。
正規表現については下記を参考にしてください。

初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」
Ruby | Rubular で ruby の正規表現を検証しながら作成 #ruby #正規表現
Rubular(正規表現を検証しながら確認できます)

電話番号の前提条件

電話番号には様々な組み合わせ(桁数・ハイフン・括弧)がありますが今回は下記を前提として表現することを目標とします。

・固定電話
 ・全部で10桁
 ・頭が0
 ・末尾は4桁
 ・ハイフンや括弧が入る可能性がある(なくても良い)

・携帯電話
 ・全部で11桁
 ・1,3桁目は0
 ・2桁目は5-9
 ・ハイフンや括弧が入る可能性がある(なくても良い)

これらを満たす電話番号のパターンは大きく分けて下記のとおりです。
(0は固定でxには任意の数字が入る)

・固定電話(10桁)
0x-xxxx-xxxx
0xx-xxx-xxxx
0xxx-xx-xxxx
0xxxx-x-xxxx

・携帯電話(11桁)
050-xxxx-xxxx
060-xxxx-xxxx
070-xxxx-xxxx
080-xxxx-xxxx
090-xxxx-xxxx

正規表現で表す(コピペでOK!!)

結論から言うと電話番号は下記のように表すことができます。

固定電話:\A0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1})[-)]?\d{4}\z
携帯電話:\A0[5789]0[-]?\d{4}[-]?\d{4}\z

正規表現の解説

\A 頭文字を表す
\d{n} n桁の数字
[-(]? “-“もしくは”(“が入る (どちらもない場合もある)
| 「または」を表す
\z 最後の文字を表す

読みやすくするために区切ってみます。
(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1}) が長くてわかりにくくしていので “|”で改行しています。

【固定電話】

【携帯電話】

Rspecでのバリデーションとテスト

ここまでを考慮して電話番号のバリデーションとテストを書きました。

class User < ApplicationRecord
  VALID_PHONE_NUMBER_REGEX = /\A0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1})[-)]?\d{4}\z|\A0[5789]0[-]?\d{4}[-]?\d{4}\z/
  validates :phone_number, presence: true, format: { with: VALID_PHONE_NUMBER_REGEX }
end
RSpec.describe User, type: :model do
  before do
    stub_const('VALID_PHONE_NUMBER_REGEX', \A0(\d{1}[-(]?\d{4}|\d{2}[-(]?\d{3}|\d{3}[-(]?\d{2}|\d{4}[-(]?\d{1})[-)]?\d{4}\z|\A0[5789]0[-]?\d{4}[-]?\d{4}\z)
  end

  context 'フォーマット' do
    let(:user) { FactoryBot.create(:user) }  # 別ファイルでUserのFactoryBotを作成しています。

    it 'phone_numberのフォーマットが適切であること' do
      expect(user.phone_number).to match(VALID_PHONE_NUMBER_REGEX)
    end
  end
end
FactoryBot.define do
  factory :user do
    phone_number { "0#{rand(0..9)}0#{rand(1_000_000..99_999_999)}" }
    # 1,3桁目が0, 全部で10-11桁の電話番号を生成
  end
end

おわりに

電話番号のバリデーションをするために電話番号を正規表現で表し、Rspecのバリデーションテストを作成しました。

今回ご紹介したテストは、テストが通るような成功パターンのみをランダムに生成しています。
本来は、前提条件を満たすパターン・満たさないパターンを明示してテストを書く必要がありますが、その点については別の機会にご紹介いたします。