読者です 読者をやめる 読者になる 読者になる

woshidan's blog

そんなことよりコードにダイブ。

RailsのModelのvalidatesメソッドを見て単純なバリデーションテストを生成するgemを書きました

github.com

表題の通り、RailsのModelのvalidatesメソッドを見て単純なバリデーションテストを生成するgemを書きました。

書いた理由

RailsのModelの基本的なバリデーションのテストは時々、Rails Tutorialやドキュメントのコピペと何が違うのだろう、これはActiveRecordの方のテストになるから書いてないよという話もある一方、書かないと心配で結局手動でテストしてしまうよね...と虚しくなってしまう時があります。

結局心配で手動でテストをするならば、黙って書けばよい気がしますが、自動テスト化の流れが遅れているというモバイルであっても、こういったサービスが出てこようとする中、自分はこの手のテストに時間を使ったり満足していたりしていていいのか、少しもやもやしました。

そうして、どうせいつか誰かに自動化されるのだったら、たまにはフレームワークから離れて、コピペ元のテンプレートを用意したらあとは自動的にてきとうなテストコードを出してくれるライブラリを書いて、自分で半自動化してみようか、ということで書きました。

まぁ、実際に使うにはテンプレートをもっと精査する必要があるのですが...。

使い方

class Sample < ActiveRecord::Base
  validates :name, length: { maximum: 32, minimum: 6 }, presence: true
  validates :id, numericality: { only_integer: true, greater_than: 0 }

  # ...
end
irb(main):001:0> Tamebou::Writer.new("../path/to/model/sample.rb").write
=============================================
class TestSample < MiniTest::Unit::TestCase
  def setup
    @sample = build(:sample)
  end

  def teardown
    # you can write some clean up code in this method
  end

  def test_valid_name_values_in_terms_of_length
    expected_values = ["aaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
    expected_values.each do |expected_value|
      @sample.name = expected_value
      assert @sample.valid?
    end
  end

  def test_invalid_name_values_in_terms_of_length
    invalid_values = ["aaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
    invalid_values.each do |invalid_value|
      @sample.name = invalid_value
      assert_not @sample.valid?
    end
  end
end
=============================================
=============================================
class TestSample < MiniTest::Unit::TestCase
  def setup
    @sample = build(:sample)
  end

  def teardown
    # you can some clean up code in this method
  end

  def test_valid_name_values_in_terms_of_presence
    expected_values = ["presence"]
    expected_values.each do |expected_value|
      @sample.name = expected_value
      assert @sample.valid?
    end
  end

  def test_invalid_name_values_in_terms_of_presence
    invalid_values = [nil]
    invalid_values.each do |invalid_value|
      @sample.name = invalid_value
      assert_not @sample.valid?
    end
  end
end
=============================================
=============================================
class TestSample < MiniTest::Unit::TestCase
  def setup
    @sample = build(:sample)
  end

  def teardown
    # you can some clean up code in this method
  end

  def test_valid_id_values_in_terms_of_numericality
    expected_values = [2, 1]
    expected_values.each do |expected_value|
      @sample.id = expected_value
      assert @sample.valid?
    end
  end

  def test_invalid_id_values_in_terms_of_numericality
    invalid_values = ["1.1", 2.0, 0]
    invalid_values.each do |invalid_value|
      @sample.id = invalid_value
      assert_not @sample.valid?
    end
  end
end
=============================================
=> #<File:../path/to/model/sample.rb (closed)>

デフォルトはMinitestのコードが出てくる設定なので、RSpec(らしい)コードを出したい場合:

Tamebou::Writer.new("../path/to/model/sample.rb", Tamebou::Writer::DefaultTemplate::RSPEC).write

setupメソッドを書き足しておきたい場合やMinitestだけどminitest-spec-railsなどを使っているので、自分で用意したテンプレートを追加痛い場合は、

Tamebou::Writer.new("../path/to/model/sample.rb", "/path/to/your_template").write

うまくパースされてないかもしれないからログを出したい場合:

Tamebou::Writer.new("../path/to/model/sample.rb", Tamebou::Writer::DefaultTemplate::RSPEC, true).write

ちなみに、専用のテンプレートが対応していない場合は、

# 現在対応していません
validates :username, :format => /\A([^@\s]+)@((?:[a-z0-9]+\.)+[a-z]{2,})\Z/i
=============================================
class TestSample < MiniTest::Unit::TestCase
  def setup
    @sample = build(:sample)
  end

  def teardown
    # you can some clean up code in this method
  end

  def test_valid_username_values_in_terms_of_format
    expected_values = ["YOU SHOULD ADD EXPECTED VALUES BY YOUR OWN"]
    expected_values.each do |expected_value|
      @sample.username = expected_value
      assert @sample.valid?
    end
  end

  def test_invalid_username_values_in_terms_of_format
    invalid_values = ["YOU SHOULD ADD UNEXPECTED VALUES BY YOUR OWN"]
    invalid_values.each do |invalid_value|
      @sample.username = invalid_value
      assert_not @sample.valid?
    end
  end
end
=============================================

のような出力が出ます。

中身

正規表現である程度書いて諦めたりしています。

色々プロダクションで動くコードじゃないから、とりあえず1日で作ってみようと手を抜いた箇所が多くてあれです。

参考にさせていただいた記事など

morizyun.github.io qiita.com qiita.com qiita.com qiita.com Rubyist Magazine - 標準添付ライブラリ紹介 【第 10 回】 ERB