【Rails】Minitestで使用するモジュールをまとめて読み込む

# はじめに
Minitestで使うモジュールが書かれた複数のファイルをまとめて読み込むソースコードを読解した記録です。

### 環境
- Rails6.1
- Ruby2.7.8

### testディレクトリの構造
```
test/
 ├─ controllers
 （省略）
 ├─support
 │   └─ hoge.rb
 │   └─ fuga.rb
 └─  └─ piyo.rb
```
- supportディレクトリ
  - モジュールが書かれたファイルをまとめたsupportディレクトリを配置する
- hoge.rb/fuga.rb/piyo.rb
  - ファイル名に対応したモジュールを記述したファイルを配置する
```
module Hoge
  def method
    （省略）
  end
end
```

# ソースコード例
```test_helper.rb
class ActiveSupport::TestCase
# 下記を追加 
  Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each do |f|
    require f
    include File.basename(f).split('.').first.camelize.constantize
  end
end
```
- test/test_helper.rb
  - テスティングのデフォルト設定を記述するファイル
  - 各テストケースでrequireして読み込む

### 読解①
```rb
class ActiveSupport::TestCase
```
- ActiveSupport::TestCaseはMinitest::Testを継承しているクラス
- Railsで使う全てのテストクラスに継承される
  - 機能テスト、結合テストのスーパークラスであるActionDispatch::IntegrationTestはActiveSupport::TestCaseを継承している
```
# 継承関係
xxxControllerTest < ActionDispatch::IntegrationTest < ActiveSupport::TestCase < Minitest::Test
```
### 読解②
```rb
  Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each do |f|
    # require f
    # include File.basename(f).split('.').first.camelize.constantize
  end
(省略)

# __FILE__ :　test_helper.rb
# File.dirname(__FILE__): test
# File.join(File.dirname(__name__), 'support', '*.rb'): test/support/*.rb
# Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each do |f| ~ end: test/support/hoge.rb、test/support/fuga.rb、test/support/piyo.rbを配列化し、順にブロック変数fに渡す
```
- `__FILE__`
  - 擬似変数
  - 現在のソースファイル名
  - フルパスとは限らないため、フルパスが必要な場合は `File.expand_path(__FILE__)` とする必要がある
  - 実行している「ファイル名のみ」を取得する場合は `File.basename(__FILE__)` で取得する 
- `File.dirname(filename)`
  - filename の一番後ろのスラッシュより前を文字列として返す
  - スラッシュを含まないファイル名に対しては "."(カレントディレクトリ)を返す
```
p File.dirname("dir/file.ext")    # => "dir"
p File.dirname("file.ext")        # => "."
``` 
https://docs.ruby-lang.org/ja/latest/method/File/s/dirname.html

- `File.join` 
  - File::SEPARATORを間に入れて文字列を連結する
```
p File.join("a","b")                        # => "a/b"
p File.join("a/","b")                       # => "a/b"
```
https://docs.ruby-lang.org/ja/latest/method/File/s/join.html


- `Dir[]`
  -  ワイルドカード（任意の文字列を指定する時に使う特殊文字）の展開を行う
  - パターンにマッチするファイル名を文字列の配列として返す
  - パターンにマッチするファイルがない場合は空の配列を返す
  - UNIX系は`*` ｇ「任意の0文字以上」、`?`が「任意の1文字」を表す２つのワイルドカードが使われる 
```
p Dir["f*", "b*"]        # => ["foo", "bar"]
```

https://docs.ruby-lang.org/ja/latest/method/Dir/s/=5b=5d.html

### 読解③
```rb
  # Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each do |f|
    require f
    include File.basename(f).split('.').first.camelize.constantize
# end

# File.basename(f): accounts.rb
# File.basename(f).split('.'): ["accounts", "rb"]
# File.basename(f).split('.').first: "accounts"
# File.basename(f).split('.').first.camelize: "Accounts"
# File.basename(f).split('.').first.camelize.constantize: Accounts
```
- `require`
  - 外部ファイルやライブラリを呼び出す
- `include`
  - モジュールをクラス内で呼び出す


- `File.basename(filename)`
  - filename の一番後ろのスラッシュに続く要素を返す
  - 引数 suffix が与えられて、かつそれが filename の末尾に一致するなら、それを取り除いたものを返す
  - サフィックス（接尾辞）を文字列で与える。`.*` という文字列を与えた場合、`*` はワイルドカードとして働き `.` を含まない任意の文字列にマッチする
```
p File.basename("ruby/ruby.c")          #=> "ruby.c"
p File.basename("ruby/ruby.c", ".c")    #=> "ruby"
p File.basename("ruby/ruby.c", ".*")    #=> "ruby"
p File.basename("ruby/ruby.exe", ".*")  #=> "ruby"
p File.basename("ruby/y.tab.c", ".*")   #=> "y.tab"
```

https://docs.ruby-lang.org/ja/latest/method/File/s/basename.html

- `camelize`
  - キャメルケースに変換
```
p camelize('product')                #=> "Product
p camelize('backoffice/session')     #=> "Backoffice::Session
p camelize('active_record'. :lower)  #=> "activeRecord"
```
https://railsdoc.com/page/camelize
- `constantize`
  - レシーバの定数表現を解決し、実際のオブジェクトを返す
```
p constantize('Module')      #=> Module
p constantize('Test::Unit')  #=> Test::Unit
```
https://railsdoc.com/page/constantize

# おわりに
Rails7.0以降であれば、config/application.rbまたはconfig/environments/*.rbに`config.autoload_paths　+= %W(#{config.root}/test/support)`のように追加すれば、test/supportディレクトリ内のファイルの自動読み込みの設定もできるようです。