正規表現間違い探しクイズシリーズです。
正規表現は単体テストを書いている場合でもバグを発見しづらいものです。 そのためレビューの時には注意深く見るようにしています。そんな中見つけた間違いのうち印象的だったものを紹介します。
問題編
仕様
Linkモデルにはurlを持ちます。urlは以下の条件を満たします。
- 入力必須
- 長さは256文字以下
- URLの形式を満たす必要がある
- プロトコルは
http
またはhttps
のみ許可される - ドメインは
www.example.com
またはblog.example.com
のみ許可される
ソースコード
ソースコードです。 今回はRuby on Railsで実装します。
class Link < ApplicationRecord validates :url, presence: true, length: {maximum: 256} validate :valid_url private def valid_url return unless url if ! url?(url) then # URLの形式を満たしていない errors.add(:url, 'は有効なURLではありません。') return end if ! %r{\Ahttp(s)?://}.match?(url) then # httpまたはhttpsで始まっていない errors.add(:url, 'のプロトコルはhttpまたはhttpsのみ指定できます。') end if ! /((www)|(blog))\.example\.com/.match?(url) then # ドメインがwww.example.comまたはblog.example.comでない errors.add(:url, 'のドメインはwww.example.comまたはblog.example.comである必要があります。') end end def url?(url) # 今回の本題ではないので省略 end end
さて、上記ソースコードには明らかな間違いがあります。どのような間違いでしょうか。また、どのように修正するべきでしょうか。(ちなみに正規表現のはじめの\A
は文字列の先頭を指します。^
と似ていますが、違いは参考のリンクを参照してください)
テスト
以下のテストはパスしています。
require 'rails_helper' RSpec.describe Link, type: :model do describe '#url' do subject { link.errors[:url] } let(:link) { Link.new(params) } let(:params) { {url: url} } before do link.valid? end context '許可されるURLの場合' do context 'プロトコルがhttpの場合' do context 'ドメインがwww.example.comの場合' do let(:url) {'http://www.example.com/path/to/something'} it { is_expected.to be_blank} end context 'ドメインがblog.example.comの場合' do let(:url) {'http://blog.example.com/path/to/something'} it { is_expected.to be_blank} end end context 'プロトコルがhttpsの場合' do context 'ドメインがwww.example.comの場合' do let(:url) {'https://www.example.com/path/to/something'} it { is_expected.to be_blank} end context 'ドメインがblog.example.comの場合' do let(:url) {'https://blog.example.com/path/to/something'} it { is_expected.to be_blank} end end end context '許可されないURLの場合' do context '許可されないプロトコルの場合' do let(:url) {'ftp://www.example.com/path/to/something'} it { is_expected.to include('のプロトコルはhttpまたはhttpsのみ指定できます。')} end context '許可されないドメインの場合' do let(:url) {'http://www.hatenablog.com/path/to/something'} it { is_expected.to include('のドメインはwww.example.comまたはblog.example.comである必要があります。')} end end end end
RSpec知らない人だと何書いてあるかわからないかと思いますが、重要な部分は、入力がhttp://www.example.com/path/to/something
だとエラーが出なくて(be_blank
)、入力がftp://www.example.com/path/to/something
だとのプロトコルはhttpまたはhttpsのみ指定できます。
というエラーが出るという部分です。
解答編
・
・
・
少し考えてから解答編を見てください
・
・
・
続きを読む