2009-06-29 85 views
5

Có thể chia nhỏ tất cả các phụ thuộc bằng cách sử dụng các giao diện chỉ để làm cho một lớp có thể kiểm tra được không? Nó liên quan đến chi phí đáng kể khi chạy vì nhiều cuộc gọi ảo thay vì các lời gọi phương thức đơn giản.kiểm tra đơn vị trong C++

Công việc phát triển thử nghiệm theo định hướng trong các ứng dụng C++ thế giới thực như thế nào? Tôi đọc làm việc hiệu quả với mã Legacy và thích nó khá hữu ích nhưng không nhận được lên đến tốc độ thực hành TDD.

Nếu tôi thực hiện tái cấu trúc, nó xảy ra rất thường xuyên mà tôi phải hoàn toàn viết lại bài kiểm tra đơn vị vì những thay đổi logic lớn. Mã của tôi thay đổi rất thường xuyên thay đổi logic cơ bản của việc xử lý dữ liệu. Tôi không thấy một cách để viết các bài kiểm tra đơn vị mà không phải thay đổi trong một quá trình tái cấu trúc lớn.

Có thể ai đó có thể chỉ cho tôi một ứng dụng mã nguồn mở C++ đang sử dụng TDD để tìm hiểu theo ví dụ.

Trả lời

5

Cập nhật: Xem this question too.

tôi chỉ có thể trả lời một số bộ phận ở đây:

là okay để phá vỡ tất cả phụ thuộc sử dụng giao diện chỉ để làm cho một lớp học kiểm chứng? Nó liên quan đến chi phí đáng kể khi chạy vì nhiều cuộc gọi ảo thay vì các lời gọi phương thức đơn giản.

Nếu hiệu suất của bạn sẽ bị ảnh hưởng quá nhiều vì nó, không (điểm chuẩn!). Nếu sự phát triển của bạn bị quá nhiều, không (ước tính thêm nỗ lực). Nếu nó có vẻ như nó sẽ không quan trọng nhiều và giúp đỡ về lâu dài và giúp bạn với chất lượng, có.

Bạn luôn có thể 'kết bạn' với các lớp thử nghiệm của mình hoặc đối tượng TestAccessor qua đó các thử nghiệm của bạn có thể điều tra nội dung trong đó. Điều đó tránh làm cho tất cả mọi thứ có thể được gửi động chỉ để thử nghiệm. (Nghe có vẻ hơi giống một chút công việc.)

Thiết kế giao diện có thể kiểm tra không dễ dàng. Đôi khi bạn phải thêm một số phương thức bổ sung để truy cập vào nội bộ chỉ để thử nghiệm. Nó làm cho bạn rạn nứt một chút nhưng nó tốt để có và thường xuyên hơn không phải là những chức năng hữu ích trong các ứng dụng thực tế là tốt, sớm hay muộn.

Nếu tôi thực hiện tái cấu trúc, nó xảy ra rất thường xuyên mà tôi phải hoàn toàn viết lại bài kiểm tra đơn vị vì những thay đổi logic lớn. Mã của tôi thay đổi rất thường xuyên thay đổi logic cơ bản của việc xử lý dữ liệu. Tôi không thấy một cách để viết các bài kiểm tra đơn vị mà không phải thay đổi trong một quá trình tái cấu trúc lớn.

Tái cấu trúc lớn bằng cách định nghĩa thay đổi rất nhiều, bao gồm cả các thử nghiệm. Hãy vui vì bạn có chúng vì chúng sẽ kiểm tra các công cụ sau khi tái cấu trúc.

Nếu bạn dành nhiều thời gian để tái cấu trúc hơn là tạo các tính năng mới, có lẽ bạn nên suy nghĩ kỹ hơn trước khi viết mã để tìm giao diện tốt hơn có thể chịu được nhiều thay đổi hơn. Ngoài ra, viết đơn vị kiểm tra trước khi giao diện được ổn định là một nỗi đau, không có vấn đề gì bạn làm.

Bạn càng có nhiều mã chống lại giao diện thay đổi nhiều, bạn càng phải thay đổi mã nhiều lần. Tôi nghĩ vấn đề của bạn nằm ở đó. Tôi đã quản lý để có giao diện đủ ổn định ở hầu hết các nơi, và refactor chỉ phần bây giờ và sau đó.

Hy vọng điều đó sẽ hữu ích.

+0

Nó không phải là tôi không nghĩ trước khi mã hóa. Rất thường các yêu cầu thay đổi sau khi khách hàng đã nhìn thấy việc thực hiện ban đầu của một tính năng mới. Một lý do khác là đôi khi không cần phải thực hiện nhanh và dơ bẩn vì lý do tiếp thị. Trong trường hợp này tôi không bận tâm với các bài kiểm tra đơn vị nhưng đôi khi việc hack nhanh chóng và dơ bẩn trở thành điều thực sự vì một lịch trình chặt chẽ. Nó không phải là dễ dàng để làm cho mess này có thể kiểm tra sau này. – frast

+0

Xin lỗi, tôi không có ý nói chính xác điều đó. ;) Tôi biết cuộc sống thực là khó khăn để làm việc với đôi khi. Quick'n bẩn hacks đi kèm với một khoản nợ, hoặc là thiếu kiểm tra, cần thiết refactoring/dọn dẹp hoặc thường là cả hai. Bạn trả bằng tiền mặt trước (công việc thích hợp) hoặc trả tiền thuê nhà sau đó (dọn dẹp). Không nhận được xung quanh đó. Thật khó để làm việc trong điều kiện mà bạn không thể ảnh hưởng đến điều đó. (Đã ở đó ...) – Macke

3

Tôi đã sử dụng macro thông thường, #if và các thủ thuật tiền xử lý khác để phụ thuộc vào mục đích thử nghiệm đơn vị trong C và C++, chính xác vì với các macro như vậy tôi không phải trả bất kỳ thời gian chạy nào chi phí khi mã được biên dịch để sản xuất thay vì thử nghiệm. Không thanh lịch, nhưng hợp lý hiệu quả.

Đối với cấu trúc lại, chúng cũng có thể yêu cầu thay đổi các thử nghiệm khi chúng quá lớn và đáng tin cậy như bạn mô tả. Tuy nhiên, tôi không thấy mình tái cấu trúc nên tất cả thường xuyên như vậy.

+1

Tôi nghĩ về giải pháp của bạn, nhưng tôi lo sợ rằng nó có thể xảy ra một cách ngẫu nhiên rằng tôi đang thử nghiệm không phải là "mã thực" vì bộ tiền xử lý xác định. Nhưng tôi nghĩ rằng nó là giá trị để thử nó. – frast

+0

Yeah, macro và các trình xử lý tiền xử lý khác là những thứ dễ vỡ trừ khi được sử dụng cẩn thận và theo các mẫu giới hạn cụ thể - đó là phần "không thanh lịch" trong câu trả lời của tôi.Macro tương đương với tiêm phụ thuộc (đôi khi cũng có thể đạt được bởi các khuôn mẫu hoặc typedef, như trong trường hợp đơn giản) là một trường hợp khá hạn chế và có kỷ luật, và đó thực sự là tất cả những gì bạn cần để cho phép bạn "loại bỏ" sự phụ thuộc của bạn cho mục đích thử nghiệm đơn vị. –

1

Câu trả lời rõ ràng sẽ là yếu tố phụ thuộc vào việc sử dụng mẫu thay vì giao diện. Tất nhiên có thể làm tổn thương biên dịch-lần (tùy thuộc vào chính xác cách bạn thực hiện nó), nhưng nó nên loại bỏ các chi phí thời gian chạy ít nhất. Một giải pháp đơn giản hơn một chút có thể là chỉ dựa vào một tập hợp các typedef, có thể được hoán đổi với một vài macro hoặc tương tự.

1

Đối với câu hỏi đầu tiên của bạn - nó hiếm khi đáng giá để phá vỡ mọi thứ chỉ vì mục đích thử nghiệm, mặc dù đôi khi bạn có thể phải phá vỡ mọi thứ trước khi cải thiện chúng. Các tiêu chí quan trọng nhất cho một sản phẩm phần mềm là nó hoạt động, không phải là nó có thể kiểm chứng được. Có thể kiểm chứng chỉ quan trọng vì nó giúp bạn tạo ra một sản phẩm ổn định hơn và hoạt động tốt hơn cho người dùng cuối của bạn.

Phần lớn phát triển theo thử nghiệm là chọn các phần nhỏ, nguyên tử của mã mà không có khả năng thay đổi để kiểm tra đơn vị. Nếu bạn phải viết lại rất nhiều bài kiểm tra đơn vị vì những thay đổi logic lớn, bạn có thể cần phải kiểm tra ở cấp độ tinh vi hơn hoặc thiết kế lại mã của bạn để mã đó ổn định hơn. Một thiết kế ổn định không nên thay đổi đáng kể theo thời gian, và thử nghiệm sẽ không giúp bạn tránh việc tái cấu trúc khối lượng lớn là điều bắt buộc. Tuy nhiên, nếu được thực hiện kiểm tra đúng có thể làm cho nó để khi bạn refactor những điều bạn có thể tự tin hơn rằng refactoring của bạn đã thành công, giả định rằng có một số xét nghiệm mà không cần phải được thay đổi.

1

Bạn có thể phá vỡ tất cả các phụ thuộc bằng cách sử dụng giao diện chỉ để làm cho lớp có thể kiểm tra được không? Nó liên quan đến chi phí đáng kể khi chạy vì nhiều cuộc gọi ảo thay vì các lời gọi phương thức đơn giản.

Tôi nghĩ rằng đó là OK để phá vỡ các phụ thuộc, vì điều đó sẽ dẫn đến giao diện tốt hơn.

Nếu tôi thực hiện tái cấu trúc, nó xảy ra rất thường xuyên mà tôi phải hoàn toàn viết lại bài kiểm tra đơn vị vì những thay đổi logic lớn. Mã của tôi thay đổi rất thường xuyên thay đổi logic cơ bản của việc xử lý dữ liệu. Tôi không thấy một cách để viết các bài kiểm tra đơn vị mà không phải thay đổi trong một quá trình tái cấu trúc lớn.

Bạn sẽ không được sử dụng các phép tái cấu trúc lớn này bằng bất kỳ ngôn ngữ nào kể từ khi kiểm tra của bạn phải thể hiện ý định thực sự của mã của bạn. Vì vậy, nếu logic thay đổi, các bài kiểm tra của bạn phải thay đổi.

Có lẽ bạn đang không thực sự làm TDD, như:

  1. Tạo một thử nghiệm thất bại
  2. Tạo mã để vượt qua các thử nghiệm
  3. Tạo một thử nghiệm thất bại
  4. Fix mã để vượt qua cả hai bài kiểm tra
  5. Rửa sạch và lặp lại cho đến khi bạn nghĩ rằng mình có đủ bài kiểm tra cho biết mã của bạn nên làm gì

Các bước này nói rằng bạn nên thực hiện những thay đổi nhỏ, chứ không phải những thay đổi lớn. Nếu bạn ở lại với thứ hai, bạn không thể thoát khỏi các nhà tái cấu trúc lớn.Không ngôn ngữ nào giúp bạn tiết kiệm được điều đó và C++ sẽ là điều tồi tệ nhất trong số đó vì thời gian biên dịch, thời gian liên kết, thông báo lỗi xấu, v.v.

Tôi thực sự đang làm việc trong phần mềm thế giới thực được viết bằng C++ với mã di sản HUGE bên dưới. Chúng tôi đang sử dụng TDD và nó thực sự giúp phát triển thiết kế của phần mềm.

0

Nếu tôi thực hiện tái cấu trúc, điều này xảy ra rất thường xuyên mà tôi phải viết lại hoàn toàn bài kiểm tra đơn vị do thay đổi logic lớn. ... Tôi không thấy một cách để viết các bài kiểm tra đơn vị mà không phải thay đổi trong một quá trình tái cấu trúc lớn.

There are multiple layers of testing và một số lớp đó sẽ không bị vỡ ngay cả sau khi thay đổi lôgic lớn. Xét nghiệm đơn vị, mặt khác, có nghĩa là để kiểm tra nội bộ của các phương pháp và các đối tượng, và sẽ cần phải thay đổi thường xuyên hơn thế. Không có gì sai, nhất thiết. Nó chỉ là cách mọi thứ.

Có [nó] được phép chia nhỏ tất cả các phụ thuộc bằng cách sử dụng giao diện chỉ để làm cho lớp có thể kiểm tra được không?

It's definitely OK to design classes to be more testable. Đó là một phần của mục đích của TDD, sau khi tất cả.

Nó liên quan đến chi phí đáng kể khi chạy vì nhiều cuộc gọi ảo thay vì lời gọi phương thức đơn giản.

Mỗi công ty đều có một số danh sách các quy tắc mà tất cả nhân viên phải tuân theo. Các công ty không ngừng đơn giản liệt kê mọi chất lượng tốt mà họ có thể nghĩ đến ("nhân viên của chúng tôi có hiệu quả, có trách nhiệm, đạo đức và không bao giờ cắt góc"). Nhiều công ty thông minh hơn thực sự xếp thứ tự ưu tiên của họ. Nếu ai đó đưa ra một cách phi đạo đức để có hiệu quả, công ty có làm được không? Các công ty tốt nhất không chỉ in các tài liệu quảng cáo nói rằng các ưu tiên được xếp hạng như thế nào, nhưng họ cũng đảm bảo việc quản lý tuân theo thứ hạng.

Hoàn toàn có thể cho một chương trình có hiệu quả và dễ kiểm tra. Tuy nhiên có những lúc bạn cần chọn cái nào quan trọng hơn. Đây là một trong những thời điểm. Tôi không biết hiệu quả quan trọng đối với bạn và chương trình của bạn như thế nào, nhưng bạn cũng vậy. Vì vậy, "bạn muốn có một chương trình chậm, được kiểm tra tốt hay một chương trình nhanh mà không có phạm vi kiểm tra tổng thể?"

+0

Bạn thực hiện những điểm rất tốt. Đối với câu hỏi cuối cùng của bạn, tôi nghĩ chúng ta nên suy nghĩ về hiệu suất so với mức độ kiểm tra từ từng trường hợp. Không phải tất cả các phần của ứng dụng của chúng tôi đều cần hiệu suất tối đa. – frast

0

Nó liên quan đến chi phí đáng kể tại thời gian chạy là vì nhiều cuộc gọi ảo thay vì lời gọi phương thức đơn giản.

Hãy nhớ rằng đó chỉ là phí gọi điện thoại ảo nếu bạn truy cập phương thức thông qua con trỏ (hoặc ref.) Cho bạn giao diện hoặc đối tượng. Nếu bạn truy cập vào phương thức thông qua một đối tượng cụ thể trong ngăn xếp, nó sẽ không có phí ảo, và nó thậm chí có thể được inlined.

Ngoài ra, không bao giờ giả định rằng phí trên không lớn trước khi lập hồ sơ cho mã của bạn. Hầu như luôn luôn, một cuộc gọi ảo là vô giá trị nếu bạn so sánh với những gì phương pháp của bạn đang làm. (Hầu hết các hình phạt đến từ không thể nội tuyến một phương pháp một dòng, không phải từ sự gián đoạn thêm của cuộc gọi).