2008-11-14 23 views
18

Sự phát triển của nhóm của tôi về TDD bao gồm những gì dường như là khởi hành từ oop truyền thống.Điều gì là quan trọng hơn, khả năng kiểm thử của mã, hoặc tuân thủ các nguyên tắc OOP?

  1. Di chuyển khỏi các lớp tự cung tự cấp Chúng tôi vẫn đóng gói dữ liệu khi thích hợp. Nhưng để giả lập bất kỳ lớp trợ giúp nào, chúng ta thường tạo ra một số cách để đặt bên ngoài chúng thông qua constructor hoặc mutator.

  2. Chúng tôi không sử dụng các phương pháp riêng tư. Để tận dụng lợi thế của khung mocking của chúng tôi (RhinoMocks), các phương thức không thể là riêng tư. Đây là điều lớn nhất để "bán" cho các nhà phát triển truyền thống của chúng tôi. Và ở một mức độ nào đó tôi thấy quan điểm của họ. Tôi chỉ thử nghiệm giá trị hơn.

Suy nghĩ của bạn là gì?

Trả lời

24

OOP chỉ là một mô hình trong số nhiều khả năng. Nó không phải là một mục tiêu trong chính nó, nó là một phương tiện để kết thúc. Bạn không cần phải lập trình hướng đối tượng, không phải nếu các mô hình khác phù hợp hơn với bạn.

Và vô số người thông minh trước khi bạn nhận xét rằng kiểm tra đơn vị có xu hướng dễ dàng hơn nhiều trong ngôn ngữ hướng đối tượng hơn là hướng đối tượng, đơn giản vì đơn vị tự nhiên cho thử nghiệm một hàm. Nó không phải là một lớp (mà có thể có các phương thức riêng và tất cả các loại trạng thái lạ), nhưng là một hàm.

Khả năng kiểm tra mặt khác, không có giá trị nào. Nếu mã của bạn không thể kiểm tra, bạn không thể kiểm tra nó (rõ ràng), và nếu bạn không thể kiểm tra nó, làm thế nào bạn có thể nói rằng nó hoạt động? Vì vậy, nếu bạn phải chọn một cực hay khác, tôi chắc chắn sẽ chọn testability.

Nhưng một câu hỏi rõ ràng là, bạn có thực sự cần phải kiểm tra mọi phương pháp riêng tư không?Đây không phải là một phần của hợp đồng công khai của một lớp học, và có thể không có ý nghĩa trong sự cô lập. Các phương pháp công cộng rất quan trọng để kiểm tra vì chúng có mục đích cụ thể, chúng phải thực hiện hợp đồng rất cụ thể này, và để đối tượng ở trạng thái nhất quán và vân vân. Chúng vốn có thể thử nghiệm được, theo cách mà một phương pháp riêng có thể không. (Ai quan tâm một phương thức riêng tư nào? Nó không phải là một phần của hợp đồng lớp)

Có lẽ một giải pháp tốt hơn là chỉ cần refactor một số công cụ riêng tư vào các lớp riêng biệt. Có lẽ nhu cầu thử nghiệm các phương pháp riêng tư không lớn như bạn nghĩ.

Trên một lưu ý khác, các khuôn khổ giả mạo khác cũng cho phép bạn thử công cụ riêng tư.

Chỉnh sửa: Sau khi suy nghĩ thêm một chút nữa, tôi muốn nhấn mạnh rằng chỉ việc đặt thành viên riêng tư là một ý tưởng tồi tệ. Lý do chúng tôi có các thành viên tư nhân ở nơi đầu tiên là: Bất biến lớp phải được duy trì mọi lúc. Nó không thể cho mã bên ngoài để đưa lớp của bạn vào trạng thái không hợp lệ. Đó không chỉ là OOP, đó cũng là cảm giác thông thường. Các phương thức riêng chỉ đơn giản là cho phép bạn chi tiết hơn trong nội bộ lớp, bao gồm một số nhiệm vụ trên nhiều phương thức và như vậy, nhưng chúng thường là không bảo toàn bất biến lớp. Họ làm một nửa công việc, và sau đó dựa vào một số phương pháp riêng tư khác được gọi sau đó để làm một nửa còn lại. Và điều đó an toàn vì chúng thường không thể truy cập được. Chỉ có các phương thức lớp khác có thể gọi chúng, miễn là chúng bảo toàn được bất biến, tất cả đều tốt. Vì vậy, trong khi có, bạn thực hiện các phương pháp riêng tư có thể kiểm tra bằng cách chuyển chúng thành công cộng, bạn cũng giới thiệu một nguồn lỗi có thể không phải bị bắt dễ dàng bằng các thử nghiệm đơn vị. Bạn có thể sử dụng lớp "sai". Một lớp được thiết kế tốt luôn duy trì bất biến của nó, bất kể nó được mã bên ngoài sử dụng như thế nào. Khi bạn làm mọi thứ công khai, điều đó không còn khả thi nữa. Mã bên ngoài có thể gọi các hàm trợ giúp nội bộ có thể không được sử dụng trong ngữ cảnh đó và sẽ đưa lớp đó vào trạng thái không hợp lệ.

Và kiểm tra đơn vị không thể thực sự đảm bảo rằng điều này không xảy ra. Vì vậy, tôi muốn nói rằng bạn có nguy cơ giới thiệu một nguồn lỗi lớn hơn nhiều so với bạn có thể đã mong đợi. Tất nhiên, với định nghĩa trên của các thành viên tư nhân (những người không bảo tồn bất biến lớp), có thể chuyển một cách an toàn nhiều phương thức khác thành công, bởi vì chúng làm bảo toàn bất biến, và vì vậy không cần phải ẩn chúng khỏi mã bên ngoài. Vì vậy, điều đó có thể làm giảm bớt vấn đề của bạn, bằng cách cung cấp cho bạn ít hơn các phương pháp riêng tư, nhưng không cho phép mã bên ngoài phá vỡ lớp học của bạn, như có thể nếu mọi thứ là công khai.

+0

Hai đoạn đầu tiên trong câu trả lời của bạn dường như mâu thuẫn với nội dung sau ... – Tim

+0

Yeah , Tôi đã viết riêng phần thứ hai sau khi xem xét lại một chút. Tôi không nghĩ có bất kỳ mâu thuẫn nào. Phần đầu tiên thực sự là một sự lựa chọn giữa các thái cực (testability * hoặc * OOP?), Phần sau chỉ ra các vấn đề hoàn toàn không quan tâm đến thực hành OOP. Thỏa hiệp là chìa khóa. :) – jalf

14

tôi không quen thuộc với RhinoMocks, và trong thực tế chưa bao giờ sử dụng hoặc cần một công cụ chế nhạo, vì vậy tôi có thể cách tắt cơ sở ở đây, nhưng:

  • cần có không xung đột giữa các nguyên tắc OO và TDD, vì
  • phương pháp tư nhân không cần phải có đơn vị kiểm tra, chỉ có những công
+0

Đồng ý. Âm thanh như nhiều vấn đề với việc sử dụng công cụ chế nhạo. Testability là _increased_ với các nguyên tắc đóng gói và OO tốt. –

+0

Đó là một cách nhìn quá đơn giản. OOP là về các đối tượng đóng gói, vì vậy chúng hoạt động như một hộp đen thành mã bên ngoài. Bài kiểm tra đơn vị là về xử lý mọi thứ như một hộp màu trắng, lấy mọi thành phần nhỏ ra khỏi ngữ cảnh của nó và thử nghiệm nó một cách cô lập. Đó là một cuộc xung đột. – jalf

+0

@jalf: thiết kế hướng thử nghiệm không phải là thử nghiệm hộp trắng –

0

là một người sử dụng thường xuyên của Intellisense, yêu cầu rằng tất cả các phương pháp được công khai sẽ lái xe cho tôi điên. Tôi sẽ tuân thủ OOP trên cơ sở đó một mình, cá nhân.

1

Tiêm phụ thuộc & Đảo ngược kiểm soát là những cách khá tốt để tận dụng tối đa cả hai thế giới. Thiết kế tốt không nên hạn chế khả năng của bạn để sử dụng mã theo những cách khác nhau (kiểm tra), nó sẽ cải thiện nó.

Chúng tôi đang viết lại toàn bộ nhóm đơn để sử dụng DI/IOC ngay bây giờ để chúng tôi có thể kiểm tra chúng (sắp có hộp cáp gần bạn).

1

Bạn cũng có thể điều tra dependency injection như là một cách để biểu lộ một số các lớp helper

2

Nếu có một khuôn khổ trong đó hạn chế cho tôi từ việc sử dụng một cái gì đó giống như phương pháp riêng, tôi thả nó. Giai đoạn. Đó là một trong những điều cơ bản trong hầu hết các ngôn ngữ cần thiết.

1

Tôi sử dụng Rhino Mocks và tôi sử dụng nhiều phương pháp riêng tư. Tôi thường viết các lớp của tôi để phụ thuộc vào các giao diện hơn là các lớp khác, bởi vì điều đó làm cho việc nhạo báng dễ dàng hơn.

1

Về phạm vi lớp học, bạn có thể sử dụng internals để đơn giản hóa các khuôn khổ (đối với người dùng cuối cùng) và chỉ định trong AssemblyInfo.cs dự án khuôn khổ

[assembly: InternalsVisibleTo("MyAssembly.Tests")]
5

Thiết kế tốt OO là hơn đóng gói, hơn có phương pháp riêng.

Một trong những mùi thiết kế chính là ghép nối giữa các phần khác nhau của hệ thống.Trước khi bạn đã tiêm các lớp trợ giúp vào các đối tượng của bạn để kiểm tra chúng, các đối tượng của bạn sẽ được tiếp cận với những người trợ giúp như thế nào?

Tôi đoán là bằng cách chuyển sang tiêm phụ thuộc, bạn đã giảm khớp nối trong hệ thống của mình.

Ngoài ra còn có Open/Closed principle. Bằng cách tiêm các lớp trợ giúp bạn cho phép thay thế đa hình của hành vi.

Và nếu có một số lo ngại về việc có các phương thức không riêng tư hiển thị trên một lớp, hãy sử dụng lớp thông qua giao diện của lớp, đó là một ý tưởng hay, đúng không?

5

Tính duy trì quan trọng hơn.

Đừng bỏ lỡ điểm mà cả hai, kiểm tra đơn vị và OOP có mục tiêu chung.

1

Tôi không quen thuộc với bất kỳ xung đột nào giữa chúng. Sự gắn kết cao và khớp nối thấp là cốt lõi của OO và điều đó cũng là cốt lõi của thử nghiệm.

3

OOP là công cụ, không phải là mục tiêu

+0

Và testability là một mục tiêu, nhưng chỉ để thử nghiệm, mà là một công cụ. – Null303

+0

OOP không phải là công cụ. Nó là một thiết kế midnset. – Tim

+0

@tim: tôi đồng ý, đó là tư duy thiết kế và không phải là tính năng ngôn ngữ; nhưng như hầu hết các khái niệm lập trình, nó là một công cụ để kết thúc: các chương trình tốt hơn – Javier

0

Tại sao không chỉ sử dụng macro thay vì từ khóa riêng tư. Khi bạn biên dịch bằng "testmode", các phương thức đó là công khai. Nếu không, họ là riêng tư. Với macro, bạn sẽ vẫn nhận được cảnh báo trình biên dịch khi bạn sử dụng phương thức riêng tư của mình công khai, khi bạn biên dịch không ở chế độ thử nghiệm. Nó là thú vị để lưu ý rằng có phương pháp riêng của bạn không kiểm tra đơn vị của họ không ngụ ý một lỗi trong chương trình của bạn, mặc dù nó có khả năng tương đương với có một chức năng, "CauseBSOD" mà không bao giờ được gọi là. Đó là một chức năng bị hỏng nặng (giả sử gây ra BSOD không phải là dự định), nhưng nó không phải là một lỗi như xa như người dùng có liên quan.

9

Điều bạn đang trải qua là các thử nghiệm tác động lên thiết kế. Đó thực sự là lý do tại sao TDD chủ yếu là chiến lược thiết kế - viết các bài kiểm tra buộc thiết kế được tách rời tốt hơn, nếu bạn chú ý và biết cách đọc các dấu hiệu.

Tiêm "các vật thể trợ giúp" vào các lớp thực tế là một điều tốt. Tuy nhiên, bạn không nên nghĩ chúng là helper. Chúng là những vật thể thông thường, hy vọng ở một mức trừu tượng khác. Về cơ bản chúng là các chiến lược, cấu hình cách các chi tiết của các thuật toán cấp cao hơn được điền vào. (Tôi thường tiêm chúng bằng cách sử dụng một hàm tạo và cũng cung cấp một hàm tạo khác tự động sử dụng triển khai sản xuất mặc định, nếu có.) - chế giễu cũng có thể là quá liều, theo kinh nghiệm của tôi. Hãy xem http://martinfowler.com/articles/mocksArentStubs.html để biết một số suy nghĩ thú vị về chủ đề này.

Về phương pháp riêng tư, tôi hoàn toàn không hiểu điều này có liên quan gì đến khung mocking của bạn - thông thường, bạn nên thử giao diện, tuy nhiên, chỉ có các phương thức công khai là một phần của hợp đồng công khai.

Dù sao, các phương pháp riêng tư phức tạp là một mùi mã, theo ý kiến ​​của tôi - đó là dấu hiệu cho thấy lớp học có lẽ đang gánh chịu quá nhiều trách nhiệm, vi phạm Nguyên tắc về trách nhiệm duy nhất. Hãy nghĩ về loại lớp thực sự sẽ là không phải là là vi phạm đóng gói để công khai nó. Di chuyển phương pháp đến lớp đó (có thể tạo ra nó trên đường đi) có khả năng sẽ cải thiện thiết kế của bạn về mặt ghép và sự gắn kết.

0

Tại sao bạn không thể sử dụng các lớp bạn bè chỉ là các lớp kiểm tra? Ngoài ra, các lớp thử nghiệm của bạn có nguồn gốc từ các lớp cha mẹ có các thành viên riêng.

Làm mọi thứ công khai để phục vụ cho các công cụ kiểm tra xấu là một sai lầm trong quan điểm của tôi. Như những người khác đã nói - điều này không phải là một sự lựa chọn. Thiết kế phần mềm tốt. Kiểm tra tốt và sử dụng các công cụ tốt. Nếu bạn phải phá vỡ các phương pháp hay nhất cho một số công cụ kiểm tra thì đó là lúc để suy nghĩ lại công cụ kiểm tra của bạn ...

Các vấn đề liên quan