ぶていのログでぶログ

思い出したが吉日

MinitestをRSpec風に書く

この記事はRuby Advent Calendar 2022の12/6の記事です。


最近、Ruby on Railsを使って初めて1からWebシステムを作っている。 Railsではデフォルトで*1Minitestのテストコードが生成される。 システムを作成しはじめた当初はデフォルトであるMinitestで書いていたのだが、作業を進めていくうえでRspecのようにテストのコンテキストによりテストコードをブロックで分けたほうが書きやすいと思い始めてきた。 調べるとどうやらMinitest::SpecでRSpec風に書けることがわかったので、備忘録的にブログに残しておく。

Minitest::Spec

公式ドキュメントに書いてあるとおりrequire "minitest/autorun"するだけでよい。便利。

github.com

contextとdescribed_classが使えない

Rspecを使ったことがあると、Minitest::Specを使うとcontextとdescribed_classが使えないことに気が付く。 contextを使いたい場合は minitest-spec-contextを使うと便利だった。 あまり開発が活発ではないGemだが、そもそもコードがシンプルなのでそういうものだと思う。 described_classも使えないのだが、良い感じのGemが見つからなかったのでこれは使わない方法で書いている。

Railsで使う場合はminitest-railsを使う

RailsでMinitest::Specを使う場合は minitest-rails を使うとよい。 Railsのバージョンによって、minitest-railsのバージョンが違うので注意が必要だ。 ここではRail 7系を想定している。

# Gemfile
group :test do
  gem 'minitest-rails', '~> 7.0'
end

そしてtest_helper.rbあたりにrequire 'minitest/rails' しておくとよい。

require_relative '../config/environment'
require 'rails/test_help'
require 'minitest/rails'  # ここを追加

ここからは必須設定ではないのだが、以下の設定をするとrails gしたときに生成されるテストコードもMinitest::Specにすることができる。

# config/application.rb
module SampleApplication
  class Application < Rails::Application
    # 以下を追加
    config.generators do |g|
      g.test_framework :minitest, spec: true
    end
  end
end

rails g すると以下のようなテストコードが生成される。

❯ cat test/models/example_test.rb
require "test_helper"

describe Example do
  # it "does a thing" do
  #   value(1+1).must_equal 2
  # end
end

MinitestからMinitest::Specに書き換えてみた

実際に私が書き換えたテストコードを並べてみる。

# Minitestで書いたテストコード
class FooBarTest < ActiveSupport::TestCase
  test 'search_by_keywordに空のクエリを渡すと空の結果が返ってくること' do
    query = { keyword: nil, enabled: false, disabled: false, registered: false, unregistered: false }
    got = FooBar.search_by_keyword(query)
    assert_equal [], got
  end

  test 'search_by_keywordにキーワードを渡すと名前で検索されること' do
    query = { keyword: 'てすと', enabled: false, disabled: false, registered: false, unregistered: false }
    got = FooBar.search_by_keyword(query).map { |s| s.name }.sort
    assert_equal %w[てすと1 てすと2 てすと3 てすと4 てすと5], got
  end
end

# Minitest::Specで書いたテストコード
describe FooBar do
  describe '#search_by_keyword' do
    it '空のクエリを渡すと空の結果が返ってくること' do
      query = { keyword: nil, enabled: false, disabled: false, registered: false, unregistered: false }
      got = FooBar.search_by_keyword(query)
      assert_equal [], got
    end

    it 'キーワードを渡すと名前で検索されること' do
      query = { keyword: 'てすと', enabled: false, disabled: false, registered: false, unregistered: false }
      got = FooBar.search_by_keyword(query).map { |s| s.name }.sort
      assert_equal %w[てすと1 てすと2 てすと3 てすと4 てすと5], got
    end
end

まとめ

Minitest::Specを使うことでRSpec風に書けることが分かった。 個人的にはMinitest::Specを使うとテストのコンテキストがはっきりしてわかりやすいと思う。 しかしながら、すでに書かれたMinitestで書かれたテストを書きなおすほどのメリットがあるかといわれると、どうだろうなぁという感じもある。 なので、プロジェクトや環境によって使い分けるとよさそうに思う。

*1:rails newsしたときに作られるのでそう思っている