Nó sẽ được tự động hơn là gõ tĩnh. Gõ vịt sau đó sẽ làm công việc tương tự như các giao diện thực hiện trong các ngôn ngữ gõ tĩnh. Ngoài ra, các lớp của nó sẽ có thể sửa đổi trong thời gian chạy để khung kiểm tra có thể dễ dàng khai thác hoặc giả định các phương thức trên các lớp hiện có. Ruby là một ngôn ngữ như vậy; rspec là khung thử nghiệm hàng đầu của nó cho TDD.
Làm thế nào kiểm tra năng động gõ hỗ trợ
Với kiểu gõ động, bạn có thể tạo các đối tượng giả bằng cách đơn giản tạo một lớp có cùng giao diện (chữ ký phương thức) đối tượng cộng tác viên bạn cần để giả. Ví dụ: giả sử bạn có một số lớp đã gửi tin nhắn:
class MessageSender
def send
# Do something with a side effect
end
end
Hãy nói rằng chúng tôi có MessageSenderUser sử dụng một phiên bản của MessageSender:
class MessageSenderUser
def initialize(message_sender)
@message_sender = message_sender
end
def do_stuff
...
@message_sender.send
...
@message_sender.send
...
end
end
Lưu ý việc sử dụng ở đây của tiêm phụ thuộc , một yếu tố chính của thử nghiệm đơn vị. Chúng tôi sẽ trở lại với điều đó.
Bạn muốn kiểm tra rằng MessageSenderUser#do_stuff
các cuộc gọi gửi hai lần. Giống như bạn làm trong một ngôn ngữ được nhập tĩnh, bạn có thể tạo một MessageSender giả, đếm số lần send
được gọi. Nhưng không giống như một ngôn ngữ gõ tĩnh, bạn không cần lớp giao diện. Bạn chỉ cần tiếp tục và tạo ra nó:
class MockMessageSender
attr_accessor :send_count
def initialize
@send_count = 0
end
def send
@send_count += 1
end
end
Và sử dụng nó trong thử nghiệm của bạn:
mock_sender = MockMessageSender.new
MessageSenderUser.new(mock_sender).do_stuff
assert_equal(mock_sender.send_count, 2)
Chính nó, "gõ vịt" của một ngôn ngữ được gõ động không thêm nhiều thứ để kiểm tra so với một ngôn ngữ gõ tĩnh. Nhưng nếu các lớp không đóng, nhưng có thể được sửa đổi trong thời gian chạy thì sao? Đó là một người thay đổi cuộc chơi. Chúng ta hãy xem làm thế nào.
Điều gì sẽ xảy ra nếu bạn không phải sử dụng phép tiêm phụ thuộc để kiểm tra lớp?
Giả sử rằng MessageSenderUser sẽ chỉ sử dụng MessageSender để gửi tin nhắn và bạn không cần phải cho phép thay thế MessageSender bằng một số lớp khác. Trong một chương trình duy nhất, điều này thường là trường hợp. Chúng ta hãy viết lại MessageSenderUser để nó đơn giản tạo và sử dụng MessageSender, không có nội dung phụ thuộc.
class MessageSenderUser
def initialize
@message_sender = MessageSender.new
end
def do_stuff
...
@message_sender.send
...
@message_sender.send
...
end
end
MessageSenderUser giờ đây đơn giản hơn để sử dụng: Không ai tạo ra nó cần tạo MessageSender để sử dụng. Nó không giống như một sự cải tiến lớn trong ví dụ đơn giản này, nhưng bây giờ hãy tưởng tượng rằng MessageSenderUser được tạo ra ở nhiều nơi hoặc nó có ba phụ thuộc. Bây giờ hệ thống có rất nhiều trường hợp vượt qua chỉ để làm cho các bài kiểm tra đơn vị hài lòng, không phải vì nó nhất thiết phải cải thiện thiết kế.
Các lớp mở cho phép bạn kiểm tra mà không cần tiêm phụ thuộc
Một khung kiểm tra trong một ngôn ngữ với kiểu gõ động và các lớp mở có thể làm cho TDD khá đẹp. Đây là đoạn mã từ kiểm tra rspec cho MessageSenderUser:
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
Đó là toàn bộ bài kiểm tra. Nếu MessageSenderUser#do_stuff
không gọi MessageSender#send
chính xác hai lần, thử nghiệm này thất bại. Lớp MessageSender thực sự không bao giờ được gọi: Chúng tôi đã nói với bài kiểm tra rằng bất cứ khi nào ai đó cố gắng tạo MessageSender, họ sẽ nhận được MessageSender giả của chúng tôi. Không cần tiêm phụ thuộc.
Thật tuyệt khi làm rất nhiều trong một thử nghiệm đơn giản như vậy. Sẽ không bao giờ phải sử dụng tiêm phụ thuộc trừ khi nó thực sự có ý nghĩa đối với thiết kế của bạn.
Nhưng điều này có liên quan gì đến các lớp mở? Lưu ý cuộc gọi đến MessageSender.should_receive
. Chúng tôi đã không xác định #should_receive khi chúng tôi viết MessageSender, vậy ai đã làm? Câu trả lời là khung kiểm tra, thực hiện một số sửa đổi cẩn thận của các lớp hệ thống, có thể làm cho nó xuất hiện vì thông qua #should_receive được xác định trên mọi đối tượng. Nếu bạn nghĩ rằng sửa đổi các lớp hệ thống như thế đòi hỏi một chút thận trọng, bạn đã đúng. Nhưng đó là điều hoàn hảo cho những gì thư viện kiểm tra đang làm ở đây và các lớp mở làm cho nó có thể.