Làm giả có vi phạm nguyên tắc Mở / Đóng không?


13

Cách đây một thời gian tôi đã đọc, trên câu trả lời Stack Overflow mà tôi không thể tìm thấy, một câu giải thích rằng bạn nên kiểm tra API công khai và tác giả nói rằng bạn nên kiểm tra giao diện. Tác giả cũng giải thích rằng nếu việc triển khai phương thức thay đổi, bạn không cần phải sửa đổi trường hợp thử nghiệm, vì làm điều này sẽ phá vỡ hợp đồng để đảm bảo hệ thống được thử nghiệm hoạt động. Nói cách khác, một thử nghiệm sẽ thất bại nếu phương thức không hoạt động, nhưng không phải vì việc triển khai đã thay đổi.

Điều này kêu gọi sự chú ý của tôi khi chúng ta nói về chế giễu. Vì chế độ nhạo báng phụ thuộc rất nhiều vào các cuộc gọi kỳ vọng từ hệ thống dưới sự phụ thuộc của thử nghiệm, nên các giả lập được kết hợp chặt chẽ với việc triển khai hơn là giao diện.

Trong khi nghiên cứu mock vs stub , một số bài báo đồng ý rằng nên sử dụng sơ khai thay vì giả, vì chúng không phụ thuộc vào kỳ vọng từ các phụ thuộc, có nghĩa là bài kiểm tra không cần kiến ​​thức về hệ thống cơ bản khi thực hiện kiểm tra.

Câu hỏi của tôi sẽ là:

  1. Làm giả có vi phạm nguyên tắc mở / đóng?
  2. Có điều gì đó còn thiếu trong cuộc tranh luận có lợi cho các cuống ở đoạn cuối, khiến cho các cuống không quá tuyệt so với giả?
  3. Nếu vậy, khi nào sẽ là một trường hợp sử dụng tốt để chế nhạo và khi nào sẽ là một trường hợp sử dụng tốt để sử dụng sơ khai?


8
Since mocking relays heavily on expectation calls from system under test's dependencies...Tôi nghĩ rằng đây là nơi bạn sẽ đi. Một giả là một số đại diện nhân tạo của một hệ thống bên ngoài. Nó không đại diện cho hệ thống bên ngoài theo bất kỳ cách nào, ngoại trừ trong trường hợp nó mô phỏng hệ thống bên ngoài theo cách nó cho phép các thử nghiệm được chạy chống lại mã có phụ thuộc vào hệ thống bên ngoài nói trên. Bạn vẫn sẽ cần các bài kiểm tra tích hợp để chứng minh rằng mã của bạn hoạt động với hệ thống thực, không bị khóa.
Robert Harvey

8
Nói cách khác, giả là một thực hiện thay thế. Đó là lý do tại sao chúng tôi đã lập trình lên một giao diện ở vị trí đầu tiên, để chúng tôi có thể sử dụng giả làm công cụ thay thế cho việc triển khai thực sự. Nói cách khác, các giả được tách rời, không được kết hợp với việc thực hiện thực tế.
Robert Harvey

3
"Nói cách khác, một thử nghiệm sẽ thất bại nếu phương thức không hoạt động, nhưng không phải vì việc triển khai đã thay đổi", điều này không phải lúc nào cũng đúng. Có rất nhiều trường hợp bạn nên thay đổi cả việc thực hiện và kiểm tra của mình.
tên gì

Câu trả lời:


4
  1. Tôi không thấy lý do tại sao giả sẽ vi phạm nguyên tắc mở / đóng. Nếu bạn có thể giải thích cho chúng tôi lý do tại sao bạn nghĩ họ có thể, thì chúng tôi có thể làm giảm bớt mối quan tâm của bạn.

  2. Nhược điểm duy nhất của sơ khai mà tôi có thể nghĩ đến là chúng thường đòi hỏi nhiều công việc để viết hơn so với giả, vì mỗi một trong số chúng thực sự là một triển khai thay thế của giao diện phụ thuộc, do đó, nó thường phải cung cấp đầy đủ (hoặc hoàn thành một cách thuyết phục) thực hiện giao diện phụ thuộc. Để cho bạn một ví dụ cực đoan, nếu hệ thống con của bạn đang thử nghiệm gọi RDBMS, thì một RDBMS giả sẽ chỉ trả lời các truy vấn cụ thể được biết bởi hệ thống con được thử nghiệm, mang lại các bộ dữ liệu thử nghiệm được xác định trước. Mặt khác, một triển khai thay thế sẽ là RDBMS trong bộ nhớ đầy đủ, có thể có thêm gánh nặng khi phải mô phỏng các quirks của RDBMS máy chủ-máy khách thực tế mà bạn đang sử dụng khi sản xuất. (May mắn thay, chúng ta có những thứ như HSQLDB, vì vậy chúng ta thực sự có thể làm điều đó, nhưng vẫn,

  3. Các trường hợp sử dụng tốt để giả định là khi giao diện phụ thuộc quá phức tạp để viết một triển khai thay thế cho nó hoặc nếu bạn chắc chắn bạn sẽ chỉ viết giả một lần và không bao giờ chạm vào nó nữa. Trong những trường hợp này, hãy tiếp tục và sử dụng một bản giả nhanh và bẩn. Do đó, các trường hợp sử dụng tốt cho sơ khai (triển khai thay thế) là khá nhiều thứ khác. Đặc biệt nếu bạn thấy trước việc tham gia vào mối quan hệ lâu dài với hệ thống con đang được thử nghiệm, chắc chắn sẽ thực hiện một triển khai thay thế sẽ tốt và sạch sẽ và sẽ chỉ yêu cầu bảo trì trong trường hợp giao diện thay đổi, thay vì yêu cầu bảo trì bất cứ khi nào giao diện thay đổi bất cứ khi nào việc thực hiện hệ thống con theo thử nghiệm thay đổi.

PS Người mà bạn đang đề cập có thể là tôi, trong một trong những câu trả lời liên quan đến thử nghiệm khác của tôi ở đây trên lập trình viên.stackexchange.com, ví dụ như câu hỏi này .


an alternative implementation would be a full-blown in-memory RDBMS- Bạn không nhất thiết phải đi xa đến thế với một cuống.
Robert Harvey

@RobertHarvey tốt, với HSQLDB và H2, thực sự không quá khó để đi xa đến thế. Có lẽ khó khăn hơn để làm một cái gì đó nửa vời để không đi xa như vậy. Nhưng nếu bạn quyết định tự làm, bạn sẽ phải bắt đầu bằng cách viết một trình phân tích cú pháp SQL. Chắc chắn, bạn có thể cắt một số góc, nhưng có rất nhiều công việc . Dù sao, như tôi đã nói ở trên, đây chỉ là một ví dụ cực đoan.
Mike Nakis

9
  1. Nguyên tắc Mở / Đóng chủ yếu là về việc có thể thay đổi hành vi của một lớp mà không sửa đổi nó. Do đó, tiêm một phụ thuộc thành phần bị chế giễu trong một lớp đang thử nghiệm không vi phạm nó.

  2. Vấn đề với nhân đôi kiểm tra (mock / stub) là về cơ bản bạn đưa ra các giả định tùy ý về cách lớp học kiểm tra tương tác với môi trường của nó. Nếu những mong muốn đó là sai, tốt, bạn có thể gặp một số vấn đề khi mã được triển khai. Nếu bạn có đủ khả năng, hãy kiểm tra mã của bạn trong cùng một ràng buộc so với ràng buộc trong môi trường sản xuất của bạn. Nếu bạn không thể, hãy đưa ra các giả định ít nhất có thể và chỉ giả định / sơ khai các thiết bị ngoại vi trong hệ thống của bạn (cơ sở dữ liệu, dịch vụ xác thực, máy khách HTTP, v.v.).

Lý do hợp lệ duy nhất tại sao, IMHO, nên sử dụng gấp đôi, là khi bạn cần ghi lại các tương tác của nó với lớp được kiểm tra hoặc khi bạn cần cung cấp dữ liệu giả (cả hai kỹ thuật đều có thể làm được). Tuy nhiên, hãy cẩn thận, việc lạm dụng nó phản ánh một thiết kế xấu hoặc một thử nghiệm phụ thuộc quá nhiều vào API khi triển khai thử nghiệm.


6

Lưu ý: Tôi giả sử bạn đang định nghĩa Mock có nghĩa là "một lớp không có triển khai, chỉ là thứ bạn có thể theo dõi" và Stub là "giả một phần, hay sử dụng một số hành vi thực sự của lớp được triển khai", theo Stack này Câu hỏi tràn .

Tôi không chắc tại sao bạn nghĩ rằng sự đồng thuận là sử dụng sơ khai, ví dụ như điều ngược lại trong Tài liệu Mockito

Như thường lệ, bạn sẽ đọc cảnh báo giả một phần: Lập trình hướng đối tượng sẽ giải quyết ít phức tạp hơn bằng cách chia độ phức tạp thành các đối tượng SRPy riêng biệt, cụ thể. Làm thế nào để một phần giả phù hợp với mô hình này? Chà, nó chỉ không ... Giả một phần thường có nghĩa là sự phức tạp đã được chuyển sang một phương thức khác trên cùng một đối tượng. Trong hầu hết các trường hợp, đây không phải là cách bạn muốn thiết kế ứng dụng của mình.

Tuy nhiên, có một số trường hợp hiếm khi các giả lập một phần trở nên tiện dụng: xử lý mã bạn không thể thay đổi dễ dàng (giao diện của bên thứ 3, tái cấu trúc tạm thời mã kế thừa, v.v.) mã thiết kế.

Tài liệu đó nói rằng nó tốt hơn tôi có thể. Sử dụng giả cho phép bạn chỉ kiểm tra một lớp cụ thể đó, và không có gì khác; nếu bạn cần một phần giả để đạt được hành vi mà bạn đang tìm kiếm, có lẽ bạn đã làm gì đó sai, đang vi phạm SRP, v.v. và mã của bạn có thể trở thành một công cụ tái cấu trúc. Giả lập không vi phạm nguyên tắc đóng mở, vì dù sao chúng chỉ được sử dụng trong các thử nghiệm, chúng không phải là thay đổi thực sự đối với mã đó. Thông thường, chúng được tạo ra khi đang di chuyển bởi một thư viện như cglib.


2
Từ cùng một câu hỏi SO được cung cấp (câu trả lời được chấp nhận), đây là định nghĩa Mock / Stub mà tôi đã đề cập: Các đối tượng Mock được sử dụng để xác định kỳ vọng, ví dụ: Trong kịch bản này, tôi mong đợi phương thức A () được gọi với các tham số như vậy và như vậy. Mocks ghi lại và xác minh những kỳ vọng như vậy. Mặt khác, Stub có một mục đích khác: chúng không ghi lại hoặc xác minh các kỳ vọng, mà cho phép chúng tôi thay thế hành vi, trạng thái của đối tượng giả mạo giả mạo để sử dụng một kịch bản thử nghiệm ...
Christopher Francisco

2

Tôi nghĩ vấn đề có thể phát sinh từ giả định rằng các thử nghiệm hợp lệ duy nhất là những thử nghiệm đáp ứng thử nghiệm mở / đóng.

Dễ dàng thấy rằng thử nghiệm duy nhất quan trọng là thử nghiệm giao diện. Tuy nhiên, trong thực tế, việc kiểm tra giao diện đó thường hiệu quả hơn bằng cách kiểm tra hoạt động bên trong.

Ví dụ, gần như không thể kiểm tra bất kỳ yêu cầu tiêu cực nào, chẳng hạn như "việc triển khai sẽ không đưa ra bất kỳ ngoại lệ nào". Xem xét một giao diện bản đồ đang được thực hiện với một hashmap. Bạn muốn chắc chắn rằng hashmap đáp ứng giao diện bản đồ, mà không cần ném, ngay cả khi nó phải làm lại mọi thứ (có thể bị xúc xắc). Bạn có thể kiểm tra mọi sự kết hợp của các yếu tố đầu vào để đảm bảo chúng đáp ứng các yêu cầu về giao diện, nhưng điều đó có thể mất nhiều thời gian hơn cái chết nhiệt của vũ trụ. Thay vào đó, bạn phá vỡ sự đóng gói một chút và phát triển các giả lập tương tác chặt chẽ hơn, buộc hashmap phải thực hiện chính xác việc làm lại cần thiết để đảm bảo thuật toán khởi động lại không bị ném.

Tl / Dr. vũ trụ để xác nhận sự phù hợp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.