Rspecでインスタンスメソッドをinitializeを通過させずにテストしたい!
過去の自分の行いに対しての戒め的記事。
Notベスプラ。
こんなことありませんか?
あなたは保守運用がメインのエンジニアです。これまで問題なく普通に運用されていたシステムが実は要件が違っていた、あるいは問題が起こっていたが誰も気付かず、そのままにされていて、自分がそのシステムの担当になったときに発覚し、鬼クレーム。早急に直近発生している問題を解消しないといけない、なんて状況。
障壁
これまで不完全だった処理に少し手を加えて異常値を省く処理を加えた。
当然、既存のコードをややこしくしないためにメソッドを切り出して、インスタンスメソッドが増えるなどのはある。
そしてその追加したインスタンスメソッドテストを試したいのだが、initializeメソッドに環境に依存する処理など、通したくない処理がある。そもそもその実装が正しいかはさておき、影響範囲は少なくスピーディに対応したいとき。
module ABCDE
class FGHIJ
def initialize(dataset = 'production')
環境依存や外部APIとの通信を挟んだり、する処理
end
def new_method
XXXXX
end
end
プランA
テストの時のみ、ソースのインスタンスメソッドをクラスメソッド化させる。
module ABCDE
class FGHIJ
def initialize(dataset = 'production')
環境依存や外部APIとの通信を挟んだり、する処理
end
def self.new_method
XXXXX
end
end
テスト。
require 'spec_helper'
module ABCDE
RSpec.describe ABCDE::FGHIJ do
describe 'new_method' do
it 'hogehogehoge' do
expect(ABCDE::FGHIJ.new_method)....
end
end
end
テストは実行できる。一応、新しく自分が書いた部分のメソッドのテストはできる。
だがそもそもテストのためにソースコードを改変するのはキングオブナンセンス。
プランB
allocateメソッドを利用する。
allocateを使うと指定したクラスのインスタンスを生成しつつ、initializeを呼ばないということができる!!
require 'spec_helper'
module ABCDE
RSpec.describe ABCDE::FGHIJ do
describe 'new_method' do
it 'hogehogehoge' do
expect(ABCDE::FGHIJ.allocate.new_method)....
end
end
end
これによりソースコードをいじることなく、テストを実行でき、initializeを通らなくて済む!
まとめ
プランBでなんとかその場を乗り切ったが、本来はその時点で設計や、前任者を既存のコードの問題を抑え込むのが最高のソリューションではある。ただし、自身の実力や置かれている状況によっては、その選択はむしろリスクとなりうることもある。自分の対応がナンセンスであることを認識しつつも、既に成立して回っているビジネスを滞りなく回し続けるためにはミニマムソリューションを選択することもある。