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桁の数字
[-(]?: "-"もしくは"("が入る (どちらもない場合もある)
|: or
\z: 最後の文字

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

【固定電話】

【携帯電話】

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

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

User.rb
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
user_spec.rb
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

@jnchitoさんに頂いたコメントを参考に下記を追加しました。(2020/6/22)

spec/factories/users.rb
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