Statik olarak yazmak yerine dinamik olarak olurdu . Ördek yazması , arabirimlerin statik olarak yazılan dillerde yaptığı işi yapar. Ayrıca, sınıfları çalışma zamanında değiştirilebilir, böylece bir test çerçevesi mevcut sınıflar üzerinde yöntemleri kolayca saplayabilir veya alay edebilir. Ruby böyle bir dildir; rspec , TDD için ilk test çerçevesidir.
Dinamik yazım testi nasıl yardımcı olur?
Dinamik yazarak, alay etmek istediğiniz ortak çalışan nesneyle aynı arabirime (yöntem imzaları) sahip bir sınıf oluşturarak sahte nesneler oluşturabilirsiniz. Örneğin, mesaj gönderen bir sınıfınız olduğunu varsayalım:
class MessageSender
def send
# Do something with a side effect
end
end
Diyelim ki bir MessageSender örneği kullanan bir MessageSenderUser var:
class MessageSenderUser
def initialize(message_sender)
@message_sender = message_sender
end
def do_stuff
...
@message_sender.send
...
@message_sender.send
...
end
end
Burada bağımlılık enjeksiyonunun , ünite testinin bir elyafının kullanımına dikkat edin. Buna geri döneceğiz.
MessageSenderUser#do_stuff
Aramaların iki kez gönderildiğini test etmek istiyorsunuz . Tıpkı statik olarak yazılmış bir dilde yaptığınız gibi, kaç kez send
çağrıldığını sayan sahte bir MessageSender oluşturabilirsiniz . Ancak statik olarak yazılmış bir dilden farklı olarak, arabirim sınıfına ihtiyacınız yoktur. Sadece devam edin ve oluşturun:
class MockMessageSender
attr_accessor :send_count
def initialize
@send_count = 0
end
def send
@send_count += 1
end
end
Ve testinizde kullanın:
mock_sender = MockMessageSender.new
MessageSenderUser.new(mock_sender).do_stuff
assert_equal(mock_sender.send_count, 2)
Tek başına, dinamik olarak yazılan bir dilin "ördek yazması", statik olarak yazılan bir dile kıyasla teste o kadar fazla katkıda bulunmaz. Peki ya sınıflar kapatılmaz, ancak çalışma zamanında değiştirilebilirse ne olur? Bu bir oyun değiştirici. Nasıl olduğunu görelim.
Bir sınıfı test edilebilir yapmak için bağımlılık enjeksiyonu kullanmak zorunda kalmazsanız ne olur?
MessageSenderUser öğesinin yalnızca ileti göndermek için MessageSender'ı kullanacağını ve MessageSender'ın başka bir sınıfla değiştirilmesine izin vermenize gerek olmadığını varsayalım. Tek bir programda bu genellikle böyledir. İleti bağımlılığı olmadan bir MessageSender oluşturup kullanması için MessageSenderUser'ı yeniden yazalım.
class MessageSenderUser
def initialize
@message_sender = MessageSender.new
end
def do_stuff
...
@message_sender.send
...
@message_sender.send
...
end
end
MessageSenderUser artık kullanımı daha basit: Bunu oluşturan hiç kimsenin kullanması için bir MessageSender oluşturması gerekmiyor. Bu basit örnekte büyük bir gelişme gibi görünmüyor, ancak şimdi MessageSenderUser'ın birden fazla yerde oluşturulduğunu veya üç bağımlılığı olduğunu hayal edin. Şimdi sistem, ünite testlerini mutlu etmek için etrafta çok fazla geçiş örneğine sahip, çünkü tasarımı mutlaka geliştiriyor.
Açık sınıflar bağımlılık enjeksiyonu olmadan test etmenizi sağlar
Dinamik yazım ve açık sınıfları olan bir dilde yapılan test çerçevesi TDD'yi oldukça güzel hale getirebilir. MessageSenderUser için rspec testinden bir kod snippet'i:
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
Bütün test bu. Eğer MessageSenderUser#do_stuff
çağırmak değil MessageSender#send
tam iki kez, bu test başarısız olur. Gerçek MessageSender sınıfı asla çağrılmaz: Teste, birisi bir MessageSender oluşturmaya çalıştığında, bunun yerine sahte MessageSender'ımızı almaları gerektiğini söyledik. Bağımlılık enjeksiyonuna gerek yoktur.
Bu kadar basit bir testte çok şey yapmak güzel. Tasarımınız için gerçekten mantıklı olmadığı sürece bağımlılık enjeksiyonunu kullanmak zorunda kalmamak daha hoştur.
Fakat bunun açık sınıflarla ne ilgisi var? Çağrıyı not edin MessageSender.should_receive
. MessageSender'ı yazarken #should_receive tanımlayamadık, kim yaptı? Yanıt, sistem sınıflarında bazı dikkatli değişiklikler yapan test çerçevesinin, #should_receive aracılığıyla her nesnede tanımlandığı gibi görünmesini sağlayabilmesidir. Sistem sınıflarını bu şekilde değiştirmenin biraz dikkat gerektirdiğini düşünüyorsanız, haklısınız. Ancak test kütüphanesinin burada yaptığı şey için mükemmel bir şey ve açık sınıflar bunu mümkün kılıyor.