静的に型付けされるのではなく、動的に型付けされます。 その後、ダックタイピングは、インターフェースが静的に型付けされた言語で行うのと同じ働きをします。また、テストフレームワークが既存のクラスのメソッドを簡単にスタブまたはモックできるように、そのクラスは実行時に変更可能です。Rubyはそのような言語の1つです。rspecは、TDDの主要なテストフレームワークです。
動的型付けがテストをどのように支援するか
動的型付けを使用すると、モックする必要があるコラボレーターオブジェクトと同じインターフェイス(メソッドシグネチャ)を持つクラスを作成するだけで、モックオブジェクトを作成できます。たとえば、メッセージを送信するクラスがあったとします。
class MessageSender
def send
# Do something with a side effect
end
end
MessageSenderのインスタンスを使用するMessageSenderUserがあるとします。
class MessageSenderUser
def initialize(message_sender)
@message_sender = message_sender
end
def do_stuff
...
@message_sender.send
...
@message_sender.send
...
end
end
単体テストの定番である依存性注入のここでの使用に注意してください。戻ってきます。
MessageSenderUser#do_stuff
呼び出しが2回送信されることをテストする必要があります。静的に型付けされた言語の場合と同じように、send
呼び出された回数をカウントするモックMessageSenderを作成できます。ただし、静的に型付けされた言語とは異なり、インターフェイスクラスは必要ありません。先に進んで作成します。
class MockMessageSender
attr_accessor :send_count
def initialize
@send_count = 0
end
def send
@send_count += 1
end
end
そして、あなたのテストでそれを使用してください:
mock_sender = MockMessageSender.new
MessageSenderUser.new(mock_sender).do_stuff
assert_equal(mock_sender.send_count, 2)
動的に型付けされた言語の「アヒル型付け」自体は、静的に型付けされた言語と比較して、テストにそれほど多くのことを加えません。しかし、クラスが閉じていなくても、実行時に変更できる場合はどうなりますか?それはゲームチェンジャーです。方法を見てみましょう。
クラスをテスト可能にするために依存性注入を使用する必要がなかった場合はどうなりますか?
MessageSenderUserがMessageSenderを使用してメッセージを送信するだけで、MessageSenderを他のクラスで置き換えることを許可する必要がないとします。これは、単一のプログラム内でよく見られます。MessageSenderUserを書き換えて、依存関係の注入なしでMessageSenderを作成して使用するようにします。
class MessageSenderUser
def initialize
@message_sender = MessageSender.new
end
def do_stuff
...
@message_sender.send
...
@message_sender.send
...
end
end
MessageSenderUserの使用が簡単になりました。作成するユーザーがMessageSenderを作成する必要はありません。この単純な例では大きな改善のようには見えませんが、MessageSenderUserが複数の場所で作成されているか、3つの依存関係があることを想像してください。これで、システムはユニットテストを円滑に進めるためだけに多くのインスタンスを渡しています。必ずしもデザインが改善されるわけではありません。
オープンクラスを使用すると、依存関係の注入なしでテストできます
動的型付けとオープンクラスを備えた言語のテストフレームワークは、TDDを非常に優れたものにすることができます。MessageSenderUserのrspecテストからのコードスニペットは次のとおりです。
mock_message_sender = mock MessageSender
MessageSender.should_receive(:new).and_return(mock_message_sender)
mock_message_sender.should_receive(:send).twice.with(no_arguments)
MessageSenderUser.new.do_stuff
これがテスト全体です。MessageSenderUser#do_stuff
がMessageSender#send
正確に2回呼び出されない場合、このテストは失敗します。実際のMessageSenderクラスは呼び出されません。テストでは、誰かがMessageSenderを作成しようとするたびに、代わりにモックMessageSenderを取得するように指示しました。依存性注入は必要ありません。
このようなより単純なテストで多くのことを行うのは良いことです。実際にデザインにとって意味のあるものでない限り、依存性注入を使用する必要がないほうがより良いです。
しかし、これはオープンクラスとどう関係しているのでしょうか?への呼び出しに注意してくださいMessageSender.should_receive
。MessageSenderの作成時に#should_receiveを定義しなかったので、誰が定義しましたか?答えは、システムクラスを注意深く変更したテストフレームワークは、#should_receiveがすべてのオブジェクトで定義されているように見せかけることができるということです。このようにシステムクラスを変更する際に注意が必要だと思う場合は、そのとおりです。しかし、これは、テストライブラリがここで行っていることに対して完璧なものであり、オープンクラスはそれを可能にします。