{
    "componentChunkName": "component---src-templates-blog-post-js",
    "path": "/blog/20211127/",
    "result": {"data":{"site":{"siteMetadata":{"title":"Juicy Blog 🍋"}},"markdownRemark":{"id":"471e9f76-b44c-560c-98c1-afc798c0094e","excerpt":"最近ちゃんとRSpecを書いていないなぁと思うことが多く、ついつい後回しにしがちなテスト。\nTDDを意識して書いていきたいところですが、今回はファイルのアップロードまわりで知見がたまってきたのでまとめて公開します。 ほぼ空のビデオファイルを作成する 自作のRails…","html":"<p>最近ちゃんとRSpecを書いていないなぁと思うことが多く、ついつい後回しにしがちなテスト。\nTDDを意識して書いていきたいところですが、今回はファイルのアップロードまわりで知見がたまってきたのでまとめて公開します。</p>\n<h3>ほぼ空のビデオファイルを作成する</h3>\n<p>自作のRailsアプリケーションで動画ファイルのアップロード機能を実装するとき<a href=\"https://sample-videos.com/\">Sample Videos</a>で公開されているファイルも最低1MBからなのでGitにコミットすることがはばかれます。\nそこでFFmpegを使ってほぼ空のビデオファイルを作成できます。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">ffmpeg -f lavfi -i color=size=10x10:rate=25:color=black -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -t 1 output.mp4</code></pre></div>\n<p>ちなみにこのときのファイルサイズは<code class=\"language-text\">3.6 kB</code>でした。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">$ stat output.mp4 | grep Size:\n  Size: 3555      \tBlocks: 8          IO Block: 4096   regular file</code></pre></div>\n<p><a href=\"https://stackoverflow.com/a/46370114\">https://stackoverflow.com/a/46370114</a></p>\n<h3>ほぼ空の画像を作成する</h3>\n<p>画像ファイル程度ならGitのコミット特に影響はないと思いますが、念の為。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">convert -size 1x1 xc:white white.png</code></pre></div>\n<p>このときのファイルサイズは<code class=\"language-text\">258 bytes</code>でした。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">$ stat white.png | grep Size:\n  Size: 258       \tBlocks: 8          IO Block: 4096   regular file</code></pre></div>\n<p><a href=\"https://stackoverflow.com/a/39504523\">https://stackoverflow.com/a/39504523</a></p>\n<h3>空のバイナリを作成する</h3>\n<p>上記の方法を調べていた後に判明したのですが、Railsの<a href=\"https://edgeapi.rubyonrails.org/classes/ActionDispatch/TestProcess/FixtureFile.html\"><code class=\"language-text\">fixture_file_upload</code></a>は上記のような方法を使わなくても影響がなさそうなので、空のテキストファイルは<code class=\"language-text\">touch</code>などでも作れますが、空のバイナリファイルを作る方法も調べておきます。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">dd if=/dev/zero of=binary.dat bs=1c count=1</code></pre></div>\n<p><a href=\"https://stackoverflow.com/a/8831573\">https://stackoverflow.com/a/8831573</a></p>\n<h3>FactoryGirl(FactoryBot)で<code class=\"language-text\">file_fixture</code>を使えるようにする</h3>\n<div class=\"gatsby-highlight\" data-language=\"ruby\"><pre class=\"language-ruby\"><code class=\"language-ruby\">FactoryBot<span class=\"token punctuation\">.</span>define <span class=\"token keyword\">do</span>\n  factory <span class=\"token symbol\">:article</span> <span class=\"token keyword\">do</span>\n    title <span class=\"token punctuation\">{</span> <span class=\"token string-literal\"><span class=\"token string\">'test'</span></span> <span class=\"token punctuation\">}</span>\n    image <span class=\"token punctuation\">{</span> Rack<span class=\"token double-colon punctuation\">::</span>Test<span class=\"token double-colon punctuation\">::</span><span class=\"token class-name\">UploadedFile</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">new</span><span class=\"token punctuation\">(</span>Rails<span class=\"token punctuation\">.</span>root<span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span><span class=\"token string-literal\"><span class=\"token string\">'spec/fixtures/dummy.jpg'</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string-literal\"><span class=\"token string\">'image/png'</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span> <span class=\"token comment\"># too bad!</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span></code></pre></div>\n<p>今回の本題なのですが、上記のダミーファイルを使って<code class=\"language-text\">factory</code>を定義しようとすると動きはするのですがやたら長ったらしくて見苦しいです。\n本来RSpecには<a href=\"https://relishapp.com/rspec/rspec-rails/docs/file-fixture\"><code class=\"language-text\">file_fixture</code></a>というメソッドが定義されているため理想としては:</p>\n<div class=\"gatsby-highlight\" data-language=\"ruby\"><pre class=\"language-ruby\"><code class=\"language-ruby\">FactoryBot<span class=\"token punctuation\">.</span>define <span class=\"token keyword\">do</span>\n  factory <span class=\"token symbol\">:article</span> <span class=\"token keyword\">do</span>\n    title <span class=\"token punctuation\">{</span> <span class=\"token string-literal\"><span class=\"token string\">'test'</span></span> <span class=\"token punctuation\">}</span>\n    image <span class=\"token punctuation\">{</span> file_fixture<span class=\"token punctuation\">(</span><span class=\"token string-literal\"><span class=\"token string\">'dummy.png'</span></span><span class=\"token punctuation\">,</span> <span class=\"token string-literal\"><span class=\"token string\">'image/png'</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span> <span class=\"token comment\"># neat!</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span></code></pre></div>\n<p>ただし<code class=\"language-text\">factory</code>には<code class=\"language-text\">file_fixture</code>が定義されていないためこのままだと当然<code class=\"language-text\">NoMethodError</code>が返ってきてしまいます。</p>\n<p><a href=\"https://github.com/thoughtbot/factory_bot/issues/1282#issuecomment-659707488\">https://github.com/thoughtbot/factory_bot/issues/1282#issuecomment-659707488</a></p>\n<p>ありがたいことに作りかけのPRではあるものの、コードが共有されていたのでこちらを組み込んでみましょう。</p>\n<div class=\"gatsby-highlight\" data-language=\"ruby\"><pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">module</span> <span class=\"token class-name\">FactoryBot</span>\n  <span class=\"token keyword\">class</span> <span class=\"token operator\">&lt;&lt;</span> <span class=\"token keyword\">self</span>\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">file_fixture_path</span></span>\n      Rails<span class=\"token punctuation\">.</span>root<span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span><span class=\"token string-literal\"><span class=\"token string\">\"spec\"</span></span><span class=\"token punctuation\">,</span> <span class=\"token string-literal\"><span class=\"token string\">\"fixtures\"</span></span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">end</span>\n  <span class=\"token keyword\">end</span>\n\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">Evaluator</span>\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">file_fixture</span></span><span class=\"token punctuation\">(</span>filename<span class=\"token punctuation\">,</span> mime_type <span class=\"token operator\">=</span> <span class=\"token keyword\">nil</span><span class=\"token punctuation\">,</span> binary <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">if</span> FactoryBot<span class=\"token punctuation\">.</span>file_fixture_path<span class=\"token punctuation\">.</span>present<span class=\"token operator\">?</span>\n        path <span class=\"token operator\">=</span> <span class=\"token class-name\">Pathname</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">new</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">File</span><span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>FactoryBot<span class=\"token punctuation\">.</span>file_fixture_path<span class=\"token punctuation\">,</span> filename<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n        <span class=\"token keyword\">if</span> path<span class=\"token punctuation\">.</span>exist<span class=\"token operator\">?</span>\n          Rack<span class=\"token double-colon punctuation\">::</span>Test<span class=\"token double-colon punctuation\">::</span><span class=\"token class-name\">UploadedFile</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">new</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">,</span> mime_type<span class=\"token punctuation\">,</span> binary<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">else</span>\n          msg <span class=\"token operator\">=</span> <span class=\"token string-literal\"><span class=\"token string\">\"the directory '%s' does not contain a file named '%s'\"</span></span>\n          Kernel<span class=\"token punctuation\">.</span><span class=\"token keyword\">raise</span> ArgumentError<span class=\"token punctuation\">,</span> msg <span class=\"token operator\">%</span> <span class=\"token punctuation\">[</span>file_fixture_path<span class=\"token punctuation\">,</span> filename<span class=\"token punctuation\">]</span>\n        <span class=\"token keyword\">end</span>\n      <span class=\"token keyword\">else</span>\n        Kernel<span class=\"token punctuation\">.</span><span class=\"token keyword\">raise</span> <span class=\"token string-literal\"><span class=\"token string\">\"to use the file_fixture helper you must set FactoryBot.file_fixture_path='path/to/fixture_files'\"</span></span>\n      <span class=\"token keyword\">end</span>\n    <span class=\"token keyword\">end</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span></code></pre></div>\n<p>これで無事テストが動くようになりました。</p>","frontmatter":{"title":"ActiveStorageでRSpecのtips","date":"November 27, 2021","description":"fixture_file_uploadや空の動画ファイルを生成する方法など"}},"previous":{"fields":{"slug":"/blog/20211107/"},"frontmatter":{"title":"Long Live Pi"}},"next":{"fields":{"slug":"/blog/20220305/"},"frontmatter":{"title":"Sourcegraphを導入してみる"}}},"pageContext":{"id":"471e9f76-b44c-560c-98c1-afc798c0094e","previousPostId":"92953565-2afe-51fe-8ebd-954c300e6c81","nextPostId":"0ec29fe4-6be7-53dc-b91b-d8ada17156b7"}},
    "staticQueryHashes": ["2785349746","2841359383"]}