Làm thế nào để bạn đơn vị thử nghiệm phương pháp riêng tư?


479

Tôi đang xây dựng một thư viện lớp sẽ có một số phương thức công khai & riêng tư. Tôi muốn có thể đơn vị kiểm tra các phương thức riêng tư (chủ yếu là trong khi phát triển, nhưng nó cũng có thể hữu ích cho việc tái cấu trúc trong tương lai).

cách chính xác để làm điều này là gì?


3
Tôi có thể đang thiếu một cái gì đó, hoặc có thể chỉ là câu hỏi này, vâng ... pre-historicvề mặt Internet, nhưng việc kiểm tra đơn vị các phương thức riêng tư hiện dễ dàng và dễ dàng, với Visual Studio tạo ra các lớp truy cập cần thiết khi cần và điền trước logic kiểm tra với các đoạn mã gần với những gì người ta có thể mong muốn cho các thử nghiệm chức năng đơn giản. Xem ví dụ. msdn.microsoft.com/en-us/l
Library / ms184807% 28VS.90% 29.aspx

3
Đây có vẻ là một bản sao gần như của stackoverflow.com/questions/34571/ .
Raedwald

Người hỏi có thể không sử dụng phòng thu trực quan
Dave

3
Đừng kiểm tra đơn vị nội bộ: blog.ploeh.dk/2015/09/22/unit-testing-iternals
Mark Seemann

Câu trả lời:


122

Nếu bạn đang sử dụng .net, bạn nên sử dụng InternalsVisibleToAttribution .


86
Kinh quá. Điều này được biên dịch vào hội đồng phát hành của bạn.
Jay

14
@Jay - không thể sử dụng #if DEBUGmột InternalsVisibleTothuộc tính xung quanh thuộc tính để làm cho nó không áp dụng để phát hành mã?
mpontillo

20
@Mike, bạn có thể, nhưng sau đó bạn chỉ có thể chạy thử nghiệm đơn vị trên mã gỡ lỗi, không phát hành mã. Vì Mã phát hành được tối ưu hóa, bạn có thể thấy các hành vi khác nhau và thời gian khác nhau. Trong mã đa luồng, điều này có nghĩa là các bài kiểm tra đơn vị của bạn sẽ không phát hiện chính xác các điều kiện cuộc đua. Tốt hơn nhiều là sử dụng sự phản chiếu thông qua đề xuất của @ AmazedSaint bên dưới hoặc sử dụng PrivateObject / PrivateType tích hợp. Điều này cho phép bạn thấy các công ty tư nhân trong các bản dựng Phát hành giả định rằng khai thác thử nghiệm của bạn đang chạy với sự tin tưởng hoàn toàn (mà MSTest chạy tại địa phương)
Jay

121
Tôi đang thiếu gì? Tại sao đây sẽ là câu trả lời được chấp nhận khi nó không thực sự trả lời câu hỏi cụ thể về thử nghiệm phương pháp riêng tư? InternalsVisibleTo chỉ hiển thị các phương thức được đánh dấu là nội bộ và không hiển thị các phương thức riêng tư theo yêu cầu của OP (và lý do tôi đã hạ cánh ở đây). Tôi đoán tôi cần tiếp tục sử dụng PrivateObject như được trả lời bởi Seven?
Darren Lewis

6
@ Jay Tôi biết điều này là hơi muộn sắp tới, nhưng một lựa chọn là sử dụng một cái gì đó giống như #if RELEASE_TESTxung quanh InternalsVisibleTonhư Mike gợi ý, và tạo một bản sao của phiên bản build cấu hình của bạn mà định nghĩa RELEASE_TEST. Bạn có thể kiểm tra mã phát hành của mình bằng các tối ưu hóa, nhưng khi bạn thực sự xây dựng để phát hành, các thử nghiệm của bạn sẽ bị bỏ qua.
Shaz

349

Nếu bạn muốn đơn vị kiểm tra một phương thức riêng tư, một cái gì đó có thể sai. Các bài kiểm tra đơn vị (nói chung) có nghĩa là để kiểm tra giao diện của một lớp, nghĩa là các phương thức công khai (và được bảo vệ) của nó. Tất nhiên bạn có thể "hack" một giải pháp cho vấn đề này (ngay cả khi chỉ bằng cách công khai các phương thức), nhưng bạn cũng có thể muốn xem xét:

  1. Nếu phương pháp bạn muốn thử nghiệm thực sự đáng để thử nghiệm, thì có thể đáng để chuyển nó vào lớp của chính nó.
  2. Thêm nhiều thử nghiệm vào các phương thức công khai gọi phương thức riêng, kiểm tra chức năng của phương thức riêng. .

38
Tùy chọn 2 làm cho các bài kiểm tra đơn vị phải có kiến ​​thức về việc thực hiện cơ bản của chức năng. Tôi không thích làm điều đó. Tôi thường nghĩ rằng các bài kiểm tra đơn vị nên kiểm tra chức năng mà không giả định bất cứ điều gì về việc thực hiện.
Herms

15
Nhược điểm của việc thực hiện thử nghiệm là các thử nghiệm sẽ rất dễ bị hỏng nếu bạn đưa ra bất kỳ thay đổi nào đối với việc thực hiện. Và điều này là không mong muốn vì tái cấu trúc cũng quan trọng như viết các bài kiểm tra trong TDD.
JtR

30
Vâng, các bài kiểm tra được cho là sẽ phá vỡ nếu bạn thay đổi việc thực hiện. TDD có nghĩa là thay đổi các bài kiểm tra đầu tiên.
sleske

39
@sleske - Tôi không đồng ý lắm. Nếu chức năng không thay đổi, thì không có lý do nào để kiểm tra bị hỏng, vì các kiểm tra thực sự phải là kiểm tra hành vi / trạng thái, chứ không phải thực hiện. Đây là những gì jtr có ý nghĩa về nó làm cho bài kiểm tra của bạn trở nên mong manh. Trong một thế giới lý tưởng, bạn sẽ có thể cấu trúc lại mã của mình và các bài kiểm tra của bạn vẫn vượt qua, xác minh rằng việc tái cấu trúc của bạn đã không thay đổi chức năng của hệ thống.
Alconja

26
Xin lỗi vì sự ngây thơ (chưa có nhiều kinh nghiệm về thử nghiệm), nhưng không phải là ý tưởng thử nghiệm đơn vị để tự mình kiểm tra mọi mô-đun mã? Tôi thực sự không hiểu tại sao nên loại bỏ các phương pháp riêng tư khỏi ý tưởng này.
OMill

118

Nó có thể không hữu ích để kiểm tra các phương pháp riêng tư. Tuy nhiên, đôi khi tôi cũng thích gọi các phương thức riêng tư từ các phương thức thử nghiệm. Hầu hết thời gian để ngăn chặn việc sao chép mã để tạo dữ liệu thử nghiệm ...

Microsoft cung cấp hai cơ chế cho việc này:

Phụ kiện

  • Xem mã nguồn của định nghĩa lớp
  • Nhấp chuột phải vào tên của lớp
  • Chọn "Tạo truy cập riêng"
  • Chọn dự án trong đó trình truy cập sẽ được tạo => Bạn sẽ kết thúc với một lớp mới với tên foo_accessor. Lớp này sẽ được tạo động trong quá trình biên dịch và cung cấp cho tất cả các thành viên công khai.

Tuy nhiên, cơ chế đôi khi hơi khó hiểu khi thay đổi giao diện của lớp gốc. Vì vậy, hầu hết các lần tôi tránh sử dụng này.

Lớp PrivateObject Cách khác là sử dụng Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

2
Làm thế nào để bạn gọi phương thức tĩnh tư nhân?
Người sử dụng Stuper

18
Các bộ truy cập riêng không được dùng trong Visual Studio 2012 .
Ryan Gates

3
Phương pháp truy cập thử nghiệm Phương thức riêng tư không được chấp nhận từ VS 2011 trở đi. blog.msdn.com/b/visualstudioalm/archive/2012/03/08/ từ
Sanjit Misra

1
Đọc các tài liệu được tìm thấy trên trang web của Microsoft ở đây tôi không thấy bất kỳ đề cập nào về lớp PrivateObject bị phản đối. Tôi đang sử dụng MSVS 2013 và nó hoạt động như mong đợi.
stackunderflow

3
@RyanGates Giải pháp đầu tiên trên trang web mà bạn tham chiếu nói rằng giải pháp để truy cập các thành viên riêng là "Sử dụng lớp PrivateObject để hỗ trợ truy cập API nội bộ và riêng tư trong mã của bạn. Điều này được tìm thấy trong Microsoft.VisualStudio.QualityTools.UnitTestFramework. lắp ráp dll. "
stackunderflow

78

Tôi không đồng ý với triết lý "bạn chỉ nên quan tâm đến việc thử nghiệm giao diện bên ngoài". Nó giống như nói rằng một cửa hàng sửa chữa ô tô chỉ nên có các bài kiểm tra để xem nếu bánh xe quay. Vâng, cuối cùng tôi quan tâm đến hành vi bên ngoài nhưng tôi thích các bài kiểm tra nội bộ, riêng tư của riêng tôi để cụ thể hơn một chút và đi vào vấn đề. Có, nếu tôi tái cấu trúc, tôi có thể phải thay đổi một số thử nghiệm, nhưng trừ khi đó là một công cụ tái cấu trúc lớn, tôi sẽ chỉ phải thay đổi một số và thực tế là các thử nghiệm nội bộ khác (không thay đổi) vẫn hoạt động là một chỉ số tuyệt vời tái cấu trúc đã thành công.

Bạn có thể cố gắng bao gồm tất cả các trường hợp nội bộ chỉ sử dụng giao diện công cộng và về mặt lý thuyết có thể kiểm tra mọi phương thức nội bộ (hoặc ít nhất là mọi vấn đề) hoàn toàn bằng cách sử dụng giao diện công cộng nhưng cuối cùng bạn phải đứng trên đầu để đạt được điều này và kết nối giữa các trường hợp thử nghiệm được chạy qua giao diện công cộng và phần bên trong của giải pháp mà chúng được thiết kế để kiểm tra có thể khó hoặc không thể nhận ra. Đã chỉ ra, các thử nghiệm riêng lẻ đảm bảo rằng bộ máy bên trong hoạt động tốt cũng xứng đáng với những thay đổi thử nghiệm nhỏ xảy ra với tái cấu trúc - ít nhất đó là kinh nghiệm của tôi. Nếu bạn phải thực hiện các thay đổi lớn cho các thử nghiệm của mình cho mỗi lần tái cấu trúc, thì có thể điều này không có ý nghĩa gì, nhưng trong trường hợp đó, có lẽ bạn nên suy nghĩ lại về thiết kế của mình.


20
Tôi sợ tôi vẫn không đồng ý với bạn. Việc coi mỗi thành phần là một hộp đen cho phép các mô-đun được hoán đổi vào / ra mà không gặp vấn đề gì. Nếu bạn có một FooServiceviệc phải làm X, tất cả những gì bạn nên quan tâm là nó thực sự làm Xkhi được yêu cầu. Làm thế nào nó không quan trọng. Nếu có vấn đề trong lớp không thể nhận thấy qua giao diện (không chắc), thì nó vẫn hợp lệ FooService. Nếu đó là một vấn đề mà có thể nhìn thấy thông qua giao diện, một thử nghiệm trên các thành viên cộng đồng nên phát hiện ra nó. Toàn bộ điểm nên là miễn là bánh xe quay đúng, nó có thể được sử dụng như một bánh xe.
Cơ bản

5
Một cách tiếp cận chung là nếu logic bên trong của bạn đủ phức tạp để bạn cảm thấy nó yêu cầu kiểm tra đơn vị, có lẽ nó cần được trích xuất thành một loại lớp trợ giúp với giao diện chung có thể được kiểm tra đơn vị. Sau đó, lớp 'cha mẹ' của bạn có thể chỉ cần sử dụng người trợ giúp này và mọi người đều có thể được kiểm tra đơn vị một cách thích hợp.
Mir

9
@Basic: Hoàn toàn sai logic trong câu trả lời này. Trường hợp cổ điển khi bạn cần phương thức riêng là khi bạn cần một số mã được sử dụng lại bằng phương thức công khai. Bạn đặt mã này vào một số PrivMethod. Phương pháp này không nên được tiếp xúc với công chúng, nhưng cần thử nghiệm để đảm bảo rằng các phương thức công khai, dựa trên PrivMethod, có thể thực sự dựa vào nó.
Dima

6
@Dima Chắc chắn sau đó nếu có vấn đề với PrivMethod, một bài kiểm tra PubMethodnhững cuộc gọi nào PrivMethodsẽ phơi bày nó? Điều gì xảy ra khi bạn thay đổi SimpleSmtpServicethành một GmailService? Thật bất ngờ, các bài kiểm tra riêng tư của bạn đang chỉ vào mã không còn tồn tại hoặc có thể hoạt động khác đi và sẽ thất bại, mặc dù ứng dụng có thể hoạt động hoàn hảo như thiết kế. Nếu có quá trình xử lý phức tạp sẽ áp dụng cho cả người gửi email, có lẽ nó nên EmailProcessorđược sử dụng cho cả hai và được kiểm tra riêng?
Cơ bản

2
@miltonb Chúng tôi có thể xem xét điều này từ các phong cách phát triển khác nhau. WRT các bên trong, tôi không có xu hướng đơn vị kiểm tra chúng. Nếu có sự cố (như được xác định bằng các kiểm tra giao diện), bạn có thể dễ dàng theo dõi bằng cách đính kèm trình gỡ lỗi hoặc lớp quá phức tạp và nên được tách ra (với giao diện chung của đơn vị lớp mới được kiểm tra) IMHO
Basic

51

Trong những trường hợp hiếm hoi tôi muốn kiểm tra các chức năng riêng tư, tôi thường sửa đổi chúng để được bảo vệ thay vào đó và tôi đã viết một lớp con với chức năng bao bọc công khai.

Lớp:

...

protected void APrivateFunction()
{
    ...
}

...

Phân lớp để kiểm tra:

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...

1
Bạn thậm chí có thể đặt lớp con đó trong tệp kiểm tra đơn vị của bạn thay vì làm lộn xộn lớp thực. +1 cho xảo quyệt.
Tim Abell

Tôi luôn đặt tất cả các mã liên quan đến thử nghiệm trong dự án thử nghiệm đơn vị nếu có thể. Đây chỉ là mã psuedo.
Jason Jackson

2
Chức năng này không riêng tư, được bảo vệ, kết quả thực ... bạn đã làm cho mã của mình kém an toàn / tiếp xúc với các loại chức năng riêng tư của trẻ em
Chiến tranh

22

Tôi nghĩ rằng một câu hỏi cơ bản hơn nên được đặt ra là tại sao bạn lại cố gắng thử nghiệm phương pháp riêng tư ngay từ đầu. Đó là một mùi mã mà bạn đang cố kiểm tra phương thức riêng tư thông qua giao diện chung của lớp đó trong khi phương thức đó là riêng tư vì đó là một chi tiết triển khai. Người ta chỉ nên quan tâm đến hành vi của giao diện công cộng chứ không phải về cách nó được triển khai dưới vỏ bọc.

Nếu tôi muốn kiểm tra hành vi của phương thức riêng tư, bằng cách sử dụng các phép tái cấu trúc thông thường, tôi có thể trích xuất mã của nó sang một lớp khác (có thể với mức độ hiển thị của gói để đảm bảo nó không phải là một phần của API công khai). Sau đó tôi có thể kiểm tra hành vi của nó trong sự cô lập.

Sản phẩm của tái cấu trúc có nghĩa là phương thức riêng tư bây giờ là một lớp riêng biệt đã trở thành cộng tác viên của lớp ban đầu. Hành vi của nó sẽ trở nên được hiểu rõ thông qua các bài kiểm tra đơn vị của chính nó.

Sau đó tôi có thể chế giễu hành vi của nó khi tôi thử kiểm tra lớp ban đầu để sau đó tôi có thể tập trung kiểm tra hành vi của giao diện chung của lớp đó thay vì phải kiểm tra một vụ nổ kết hợp của giao diện công cộng và hành vi của tất cả các phương thức riêng tư của nó .

Tôi thấy điều này tương tự như lái xe hơi. Khi tôi lái xe, tôi không lái xe với nắp ca-pô để tôi có thể thấy rằng động cơ đang hoạt động. Tôi dựa vào giao diện mà chiếc xe cung cấp, cụ thể là bộ đếm vòng quay và đồng hồ tốc độ để biết động cơ đang hoạt động. Tôi dựa vào thực tế là chiếc xe thực sự di chuyển khi tôi nhấn bàn đạp ga. Nếu tôi muốn kiểm tra động cơ, tôi có thể kiểm tra nó một cách cô lập. : D

Tất nhiên việc thử nghiệm các phương thức riêng tư trực tiếp có thể là giải pháp cuối cùng nếu bạn có một ứng dụng cũ nhưng tôi thích mã kế thừa đó được cấu trúc lại để cho phép thử nghiệm tốt hơn. Michael Feathers đã viết một cuốn sách tuyệt vời về chính chủ đề này. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


16
Hoàn toàn sai logic trong câu trả lời này. Trường hợp cổ điển khi bạn cần phương thức riêng là khi bạn cần một số mã được sử dụng lại bằng phương thức công khai. Bạn đặt mã này vào một số PrivMethod. Phương pháp này không nên được tiếp xúc với công chúng, nhưng cần thử nghiệm để đảm bảo rằng các phương thức công khai, dựa trên PrivMethod, có thể thực sự dựa vào nó.
Dima

Có ý nghĩa trong quá trình phát triển ban đầu, nhưng bạn có muốn thử nghiệm các phương thức riêng tư trong bộ hồi quy tiêu chuẩn của mình không? Nếu vậy, nếu việc thực hiện thay đổi, nó có thể phá vỡ bộ kiểm tra. OTOH, nếu kiểm tra hồi quy của bạn chỉ tập trung vào các phương thức công khai có thể nhìn thấy bên ngoài, thì nếu phương thức riêng bị hỏng sau đó, bộ hồi quy vẫn sẽ phát hiện lỗi. Sau đó, nếu cần thiết, bạn có thể phủi bụi bài kiểm tra riêng tư cũ nếu cần.
Alex Blakemore

9
Không đồng ý, bạn chỉ nên thử nghiệm giao diện công cộng, tại sao lại cần các phương thức riêng tư. Làm cho tất cả chúng công khai trong trường hợp đó và kiểm tra tất cả chúng. Nếu bạn đang thử nghiệm các phương thức riêng tư, bạn sẽ phá vỡ quy tắc. Nếu bạn muốn kiểm tra một phương thức riêng tư và nó được sử dụng trong nhiều phương thức công khai thì nó nên được chuyển sang lớp riêng của nó và được kiểm tra riêng rẽ. Tất cả các phương thức công khai sau đó sẽ ủy quyền cho lớp mới đó. Giao diện của lớp gốc và bạn có thể xác minh hành vi chưa thay đổi và bạn có các thử nghiệm riêng cho phương thức riêng được ủy quyền.
Big Kahuna

@Big Kahuna - Nếu bạn nghĩ rằng không có trường hợp nào bạn cần phải thử nghiệm các phương thức riêng tư thì bạn chưa bao giờ làm việc với một dự án đủ lớn / phức tạp. Rất nhiều lần một chức năng công khai như xác nhận cụ thể của khách hàng kết thúc với 20 dòng chỉ gọi các phương thức riêng rất đơn giản để làm cho mã dễ đọc hơn nhưng bạn vẫn phải kiểm tra từng phương thức riêng tư. Kiểm tra 20 lần chức năng công cộng sẽ khiến việc ra mắt rất khó khăn khi thử nghiệm đơn vị không thành công.
Pedro ..Kid

1
Tôi làm việc cho một công ty FTSE 100. Tôi nghĩ rằng tôi đã thấy một số dự án phức tạp trong thời gian của tôi, cảm ơn bạn. Nếu bạn cần kiểm tra đến mức đó, thì mỗi phương thức riêng tư với tư cách là cộng tác viên riêng biệt, nên được kiểm tra riêng rẽ vì nó ngụ ý rằng họ có hành vi cá nhân cần thử nghiệm. Thử nghiệm cho đối tượng hòa giải chính sau đó chỉ trở thành thử nghiệm tương tác. Nó chỉ kiểm tra chiến lược chính xác đang được gọi. Kịch bản của bạn có vẻ như lớp đang nghi vấn không tuân theo SRP. Nó không có một lý do duy nhất để thay đổi nhưng 20 => vi phạm SRP. Có một cuốn sách của GOOS hoặc chú Bob. YMWV
Big Kahuna

17

Các loại riêng tư, nội bộ và thành viên riêng là vì một số lý do và thường bạn không muốn gây rối trực tiếp với họ. Và nếu bạn làm như vậy, rất có thể bạn sẽ phá vỡ sau này, bởi vì không có gì đảm bảo rằng những kẻ đã tạo ra các hội đồng đó sẽ giữ các triển khai riêng tư / nội bộ như vậy.

Nhưng, đôi khi, khi thực hiện một số hack / thăm dò các hội đồng biên dịch hoặc bên thứ ba, cuối cùng tôi đã muốn khởi tạo một lớp riêng hoặc một lớp với một nhà xây dựng riêng hoặc nội bộ. Hoặc, đôi khi, khi xử lý các thư viện kế thừa được biên dịch trước mà tôi không thể thay đổi - tôi cuối cùng đã viết một số thử nghiệm theo phương pháp riêng tư.

Do đó, sinh ra AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - đây là lớp trình bao bọc nhanh giúp công việc dễ dàng sử dụng các tính năng và phản xạ động C # 4.0.

Bạn có thể tạo các loại nội bộ / riêng tư như

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();

12

Vâng, bạn có thể đơn vị kiểm tra phương pháp riêng tư theo hai cách

  1. bạn có thể tạo thể hiện của PrivateObjectlớp cú pháp như sau

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
  2. Bạn có thể sử dụng sự phản chiếu.

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });

Câu trả lời tốt, nhưng đối với # 1 cú pháp của bạn là sai. Bạn cần khai báo một thể hiện PrivateClassđầu tiên và sử dụng nó. stackoverflow.com/questions/9122708/
trộm

10

Tôi cũng đã sử dụng phương thức InternalsVisibleToAttribution. Điều đáng nói là, nếu bạn cảm thấy không thoải mái khi đặt các phương thức riêng tư trước đây của mình để đạt được điều này, thì có lẽ chúng không phải là đối tượng của các bài kiểm tra đơn vị trực tiếp.

Rốt cuộc, bạn đang kiểm tra hành vi của lớp, thay vì thực hiện cụ thể - bạn có thể thay đổi cái sau mà không thay đổi cái trước và các bài kiểm tra của bạn vẫn sẽ vượt qua.


Tôi thích quan điểm về kiểm tra hành vi hơn là thực hiện. Nếu bạn buộc các bài kiểm tra đơn vị của mình thực hiện (phương thức riêng tư), thì các bài kiểm tra sẽ trở nên dễ vỡ và cần thay đổi khi việc triển khai thay đổi.
Bob Horn

9

Có 2 loại phương pháp riêng. Phương thức riêng tư tĩnh và phương pháp riêng không tĩnh (Phương pháp sơ thẩm). 2 bài viết sau đây giải thích cách kiểm tra các phương thức riêng tư bằng các ví dụ.

  1. Kiểm thử đơn vị Phương pháp riêng tĩnh
  2. Kiểm thử đơn vị Phương pháp riêng không tĩnh

Cung cấp một số ví dụ, không chỉ cung cấp một liên kết
vinculis

Trông xấu xí. Không thâm nhập. Giải pháp xấu từ MS. Tôi sốc !
alerya

Cách dễ nhất để kiểm tra các phương thức tĩnh riêng
Cấp

8

MS Test có một tính năng hay được tích hợp trong đó giúp các thành viên và phương thức riêng tư có sẵn trong dự án bằng cách tạo một tệp có tên VSCodeGenAccessors

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

Với các lớp xuất phát từ BaseAccessor

nhu la

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }

8
Các tệp gen'd chỉ tồn tại trong VS2005. Năm 2008 chúng được tạo ra đằng sau hậu trường. Và họ là một sự ghê tởm. Và tác vụ Shadow liên quan không ổn định trên máy chủ xây dựng.
Ruben Bartelink

Ngoài ra, những người truy cập cũng không được chấp nhận trong VS2012-2013.
Zephan Schroeder

5

Trên CodeProject, có một bài viết thảo luận ngắn gọn về ưu và nhược điểm của việc thử nghiệm các phương thức riêng tư. Sau đó, nó cung cấp một số mã phản chiếu để truy cập các phương thức riêng tư (tương tự như mã mà Marcus cung cấp ở trên.) Vấn đề duy nhất tôi tìm thấy với mẫu là mã không tính đến các phương thức quá tải.

Bạn có thể tìm thấy bài viết ở đây:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx


4

Khai báo chúng internal, và sau đó sử dụng InternalsVisibleToAttributeđể cho phép lắp ráp thử nghiệm đơn vị của bạn để xem chúng.


11
Tôi không thích sử dụng InternalsVisibleTo vì tôi đã đặt phương thức này ở chế độ riêng tư vì một lý do.
swilliams

4

Tôi có xu hướng không sử dụng các chỉ thị của trình biên dịch vì chúng làm lộn xộn mọi thứ một cách nhanh chóng. Một cách để giảm thiểu nó nếu bạn thực sự cần chúng là đặt chúng trong một lớp một phần và bản dựng của bạn bỏ qua tệp .cs đó khi tạo phiên bản sản xuất.


Bạn sẽ bao gồm các bộ truy cập thử nghiệm trong một phiên bản sản xuất (để kiểm tra tối ưu hóa trình biên dịch, v.v.) nhưng loại trừ chúng trong phiên bản phát hành. Nhưng tôi đang chia tóc và dù sao tôi cũng ủng hộ điều này bởi vì tôi nghĩ nên để những thứ đó ở một nơi duy nhất. Cảm ơn ý kiến.
CAD bloke

4

Bạn không nên thử nghiệm các phương thức riêng tư của mã của bạn ở nơi đầu tiên. Bạn nên kiểm tra 'giao diện công cộng' hoặc API, những thứ công khai trong các lớp học của bạn. API là tất cả các phương thức công khai mà bạn đưa ra cho những người gọi bên ngoài.

Lý do là một khi bạn bắt đầu thử nghiệm các phương thức riêng tư và nội bộ của lớp, bạn sẽ kết hợp việc thực hiện lớp của bạn (những thứ riêng tư) với các bài kiểm tra của bạn. Điều này có nghĩa là khi bạn quyết định thay đổi chi tiết thực hiện, bạn cũng sẽ phải thay đổi các bài kiểm tra của mình.

Vì lý do này, bạn nên tránh sử dụng InternalsVisibleToAtrribution.

Đây là một bài nói chuyện tuyệt vời của Ian Cooper bao gồm chủ đề này: Ian Cooper: TDD, nơi mà tất cả đã đi sai


3

Đôi khi, nó có thể là tốt để kiểm tra khai báo tư nhân. Về cơ bản, trình biên dịch chỉ có một phương thức công khai: Biên dịch (chuỗi outputFileName, chuỗi params [] sourceSFileNames). Tôi chắc rằng bạn hiểu rằng sẽ khó kiểm tra một phương pháp như vậy nếu không kiểm tra từng khai báo "ẩn"!

Đó là lý do tại sao chúng tôi đã tạo ra Visual T #: để ​​thực hiện các bài kiểm tra dễ dàng hơn. Đó là ngôn ngữ lập trình .NET miễn phí (tương thích C # v2.0).

Chúng tôi đã thêm toán tử '.-'. Nó chỉ hành xử như '.' toán tử, ngoại trừ bạn cũng có thể truy cập bất kỳ khai báo ẩn nào từ các thử nghiệm của mình mà không thay đổi bất cứ điều gì trong dự án được thử nghiệm của bạn.

Hãy xem tại trang web của chúng tôi: tảimiễn phí .


3

Tôi ngạc nhiên chưa ai nói điều này, nhưng một giải pháp tôi đã sử dụng là tạo ra một phương thức tĩnh bên trong lớp để tự kiểm tra. Điều này cho phép bạn truy cập vào mọi thứ công khai và riêng tư để kiểm tra.

Hơn nữa, trong một ngôn ngữ kịch bản (với các khả năng OO, như Python, Ruby và PHP), bạn có thể tự kiểm tra tệp khi chạy. Cách nhanh chóng tốt đẹp để đảm bảo thay đổi của bạn đã không phá vỡ bất cứ điều gì. Điều này rõ ràng tạo ra một giải pháp có thể mở rộng để kiểm tra tất cả các lớp của bạn: chỉ cần chạy tất cả chúng. (bạn cũng có thể thực hiện việc này bằng các ngôn ngữ khác với khoảng trống chính luôn chạy các bài kiểm tra của nó).


1
Mặc dù điều này là thực tế, nó không phải là rất thanh lịch. Điều này có thể tạo ra một chút hỗn độn của cơ sở mã và cũng không cho phép bạn tách các thử nghiệm của mình khỏi mã thực của bạn. Khả năng kiểm tra bên ngoài mở ra khả năng tự động kiểm tra kịch bản thay vì viết các phương thức tĩnh bằng tay.
Darren Reid

Điều này không ngăn cản bạn kiểm tra bên ngoài ... chỉ cần gọi phương thức tĩnh theo cách bạn muốn. Cơ sở mã cũng không lộn xộn ... bạn đặt tên cho phương thức cho phù hợp. Tôi sử dụng "runTests", nhưng bất cứ điều gì tương tự đều hoạt động.
rube

Quyền của bạn, nó không loại trừ việc kiểm tra bên ngoài, tuy nhiên, nó tạo ra nhiều mã hơn, nghĩa là làm cho cơ sở mã trở nên lộn xộn. Mỗi lớp có thể có rất nhiều phương thức riêng để kiểm tra, khởi tạo các biến của nó trong một hoặc nhiều hàm tạo của nó. Để kiểm tra, bạn sẽ phải viết ít nhất nhiều phương thức tĩnh như có các phương thức để kiểm tra và các phương thức kiểm tra có thể phải lớn để khởi tạo đúng giá trị. Điều này sẽ làm cho việc bảo trì mã khó khăn hơn. Như những người khác đã nói, kiểm tra hành vi của một lớp là một cách tiếp cận tốt hơn, phần còn lại nên đủ nhỏ để gỡ lỗi.
Darren Reid

Tôi sử dụng cùng một số dòng để kiểm tra như bất kỳ ai khác (thực tế ít hơn bạn sẽ đọc sau). Bạn không phải kiểm tra TẤT CẢ các phương pháp riêng tư của mình. Chỉ là những thứ cần kiểm tra :) Bạn cũng không cần kiểm tra từng cái theo một phương pháp riêng. Tôi làm điều đó với một cuộc gọi. Điều này thực sự làm cho việc bảo trì mã LESS trở nên khó khăn, vì tất cả các lớp của tôi đều có cùng một phương thức kiểm tra đơn vị ô, chạy tất cả các bài kiểm tra đơn vị riêng tư và được bảo vệ theo từng dòng. Toàn bộ khai thác kiểm tra sau đó gọi phương thức tương tự trên tất cả các lớp của tôi và tất cả bảo trì đều nằm trong lớp của tôi - kiểm tra và tất cả.
rube

3

Tôi muốn tạo một ví dụ mã rõ ràng ở đây mà bạn có thể sử dụng trên bất kỳ lớp nào mà bạn muốn kiểm tra phương thức riêng tư.

Trong trường hợp thử nghiệm của bạn, chỉ bao gồm các phương thức này và sau đó sử dụng chúng như được chỉ định.

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ this -> _ callMethod ('_ someFunctionName', mảng (param1, param2, param3));

Chỉ cần đưa ra các tham số theo thứ tự xuất hiện trong hàm riêng ban đầu


3

Đối với bất cứ ai muốn chạy các phương thức riêng tư mà không cần tất cả các fess và lộn xộn. Điều này hoạt động với bất kỳ khung thử nghiệm đơn vị nào sử dụng không có gì ngoài Reflection cũ tốt.

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

Sau đó, trong các thử nghiệm thực tế của bạn, bạn có thể làm một cái gì đó như thế này:

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");

2

MbUnit có một trình bao bọc đẹp cho cái này được gọi là Reflector.

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

Bạn cũng có thể đặt và nhận các giá trị từ các thuộc tính

dogReflector.GetProperty("Age");

Về "thử nghiệm riêng tư" tôi đồng ý rằng .. trong thế giới hoàn hảo. không có điểm nào trong việc làm bài kiểm tra đơn vị tư nhân. Nhưng trong thế giới thực, bạn có thể sẽ muốn viết các bài kiểm tra riêng thay vì tái cấu trúc mã.


4
Chỉ để biết thông tin, Reflectorđã được thay thế bởi mạnh hơn Mirrortrong Gallio / MbUnit v3.2. ( gallio.org/wiki/doku.php?id=mbunit:mirror )
Yann Trevin

2

Đây là bài viết tốt về thử nghiệm đơn vị của các phương pháp riêng tư. Nhưng tôi không chắc những gì tốt hơn, để làm cho ứng dụng của bạn được thiết kế đặc biệt để thử nghiệm (nó giống như tạo các thử nghiệm chỉ để thử nghiệm) hoặc sử dụng phản xạ để thử nghiệm. Khá chắc chắn rằng hầu hết chúng ta sẽ chọn cách thứ hai.


2

Theo tôi, bạn chỉ nên kiểm tra API công khai của bạn.

Công khai một phương thức, để đơn vị kiểm tra nó, phá vỡ đóng gói phơi bày chi tiết thực hiện.

API công khai tốt sẽ giải quyết mục tiêu ngay lập tức của mã máy khách và giải quyết hoàn toàn mục tiêu đó.


Đây phải là câu trả lời đúng IMO. Nếu bạn có nhiều phương thức riêng tư thì điều này có lẽ là do bạn có một lớp ẩn mà bạn nên thoát ra khỏi giao diện chung của chính nó.
trời

2

Tôi sử dụng lớp PrivateObject . Nhưng như đã đề cập trước đây tốt hơn để tránh thử nghiệm các phương pháp riêng tư.

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);

2
CC -Dprivate=public

"CC" là trình biên dịch dòng lệnh trên hệ thống tôi sử dụng. -Dfoo=barkhông tương đương với #define foo bar. Vì vậy, tùy chọn biên dịch này có hiệu quả thay đổi tất cả các công cụ riêng tư thành công khai.


2
Cái này là cái gì? Điều này có áp dụng cho Visual Studio không?
YeahStu

"CC" là trình biên dịch dòng lệnh trên hệ thống tôi sử dụng. "-Dfoo = bar" tương đương với "#define foo bar". Vì vậy, tùy chọn biên dịch này có hiệu quả thay đổi tất cả các công cụ riêng tư thành công khai. ha-ha!
Mark Harrison

Trong Visual Studio, đặt định nghĩa trong môi trường xây dựng của bạn.
Đánh dấu Harrison

1

Đây là một ví dụ, đầu tiên là chữ ký phương thức:

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

Đây là bài kiểm tra:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}

1

Một cách để làm điều này là có phương thức của bạn protectedvà viết một lịch thi thử kế thừa lớp của bạn để được kiểm tra. Bằng cách này, bạn cũng không chuyển phương thức của mình public, nhưng bạn cho phép thử nghiệm.


Tôi không đồng ý với điều này, bởi vì bạn cũng sẽ cho phép người tiêu dùng của mình kế thừa từ lớp cơ sở và sử dụng các chức năng được bảo vệ. Đây là một số điều bạn muốn ngăn chặn ngay từ đầu bằng cách làm cho các chức năng đó ở chế độ riêng tư hoặc nội bộ.
Nick N.

1

1) Nếu bạn có mã kế thừa thì cách duy nhất để kiểm tra các phương thức riêng là phản xạ.

2) Nếu đó là mã mới thì bạn có các tùy chọn sau:

  • Sử dụng sự phản chiếu (để phức tạp)
  • Viết kiểm thử đơn vị trong cùng một lớp (làm cho mã sản xuất trở nên xấu xí bằng cách có mã kiểm tra trong đó)
  • Tái cấu trúc và làm cho phương thức được công khai trong một số loại lớp sử dụng
  • Sử dụng chú thích @VisibleForTesting và xóa riêng tư

Tôi thích phương pháp chú thích, đơn giản nhất và ít phức tạp nhất. Vấn đề duy nhất là chúng tôi đã tăng khả năng hiển thị mà tôi nghĩ không phải là mối quan tâm lớn. Chúng ta phải luôn luôn mã hóa giao diện, vì vậy nếu chúng ta có giao diện MyService và triển khai MyServiceImpl thì chúng ta có thể có các lớp kiểm tra tương ứng đó là MyServiceTest (phương thức giao diện thử nghiệm) và MyServiceImplTest (phương thức kiểm tra riêng tư). Tất cả các máy khách dù sao cũng nên sử dụng giao diện, theo một cách nào đó, mặc dù khả năng hiển thị của phương thức riêng tư đã tăng lên nhưng điều đó không thực sự quan trọng.


1

Bạn cũng có thể khai báo nó là công khai hoặc nội bộ (với InternalsVisibleToAttribution) trong khi xây dựng ở chế độ gỡ lỗi:

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

Nó làm mờ mã, nhưng nó sẽ privateở trong bản phát hành.


0

Bạn có thể tạo phương thức thử nghiệm cho phương thức riêng tư từ Visual studio 2008. Khi bạn tạo một thử nghiệm đơn vị cho một phương thức riêng tư, một thư mục Tham khảo thử nghiệm được thêm vào dự án thử nghiệm của bạn và một trình truy cập được thêm vào thư mục đó. Bộ truy cập cũng được tham chiếu trong logic của phương pháp thử nghiệm đơn vị. Trình truy cập này cho phép kiểm tra đơn vị của bạn gọi các phương thức riêng tư trong mã mà bạn đang kiểm tra. Để biết chi tiết hãy xem

http://msdn.microsoft.com/en-us/l Library / bb385974.aspx


0

Cũng lưu ý rằng InternalsVisibleToAtrribution có yêu cầu rằng hội đồng của bạn phải được đặt tên mạnh , điều này tạo ra một loạt vấn đề nếu bạn làm việc trong một giải pháp chưa có yêu cầu đó trước đó. Tôi sử dụng accessor để kiểm tra các phương thức riêng tư. Xem câu hỏi này cho một ví dụ về điều này.


2
Không, InternalsVisibleToAttributekhông không đòi hỏi rằng hội đồng của bạn được đặt tên mạnh. Tôi hiện đang sử dụng nó cho một dự án trong đó không phải là trường hợp.
Cody Grey

1
Để làm rõ điều này: "Cả hội đồng hiện tại và hội đồng bạn bè đều không được ký hoặc cả hai phải được ký với một tên mạnh." - Từ MSDN
Hari
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.