pockestrap

Programmer's memo

gemspecのfilesとtest_filesとはなんなのか

TL;DR

test_filesは使わない。

files と test_files

gemspecで指定するこれらの挙動を説明します。

files

https://guides.rubygems.org/specification-reference/#files

パッケージに含まれるファイルを指定します。

Gem::Specification.new do |s|
  s.files = ['foo.rb', 'bar.rb']
end

このように書くと、このgemにはfoo.rbbar.rbのみが含まれます。

実際にはファイルを手書きで列挙せずに、git ls-filesを使ったりDir.globを使ったりすることが多いでしょう。

test_files

パッケージに含まれるテストファイルを指定します たとえば次のように指定できます。

Gem::Specification.new do |s|
  s.test_files = ['test/foo_test.rb']
end

しかし、この指定は推奨されていません。

I removed them since they're no longer useful or recommended to set.

https://github.com/rubygems/guides/issues/90#issuecomment-49213972

とくにdeprecateなどのマークはされていないように見えますが、ドキュメントからは削除されているし、推奨しないとも言われています。

また私の知る限りでは、test_filesを見て何かをするツールというのは聞いたことがありません。 そのため、基本的には指定する理由はないでしょう。

test_filesの罠

これがこの記事の主題なのですが、test_filesには誤解されているのではと思われる挙動があります。 それは、「test_filesに指定したファイルはfilesで指定されていなくてもパッケージに含まれる」という挙動です。

例えば次のgemspecを考えます。

# foo.gemspec

# Prepare files
files = %w[foo.rb bar.rb foo_test.rb bar_test.rb]
system("touch #{files.join(' ')}")

Gem::Specification.new do |s|
  s.name = 'foo'
  s.version = '1.0.0'
  s.authors = ['pocke']
  s.summary = 'Test test_files'

  # files にはテストファイル以外を指定
  s.files = files.grep_v(/test/)

  # test_files にはテストファイルを指定
  s.test_files = files.grep(/test/)
end

これをgem buildすると、foo-1.0.0.gemができます。 そして、このgemのパッケージの中にはfoo.rb, bar.rb, foo_test.rb, bar_test.rbの4つ全てが含まれます。

$ gem build

$ tar -xvf foo-1.0.0.gem
metadata.gz
data.tar.gz
checksums.yaml.gz

$ tar -zxvf data.tar.gz
bar.rb
bar_test.rb
foo.rb
foo_test.rb

つまり、gemのパッケージサイズを減らすためにfilesからテストファイルを除いていたとしても、test_filesを指定すると結局テストファイルがパッケージに含まれてしまいます。

実際にこのような指定がされているgemは多く見つけられます。 たとえばgraphiql-rails gemではこの指定がされており、Pull Requestを作成しました。 https://github.com/rmosolgo/graphiql-rails/pull/88

結論

test_filesは推奨されていないこと、またmisleadingとも言える挙動を持つことから、使わないほうが良いでしょう。 また、既存のgemに対しても、どのような意図でtest_filesが使われているのかを考えた上で、指定を削除すると良いと思います。

追記 2020-10-24 18:34

sue445さんにruby-jpで「手持ちのgemを調べたけど2013年11月に bundle gem したgemには test_files がありました」と教えてもらいました。 現在のbundlerではbundle gemをしてもtest_filesは生成されないので、どこかでこの挙動が変更されたのだろうと調べてみると、次のPRを見つけました。

github.com

言いたいことはこのPRのdescriptionに全部書いてあるので、一読することをおすすめします。