2008-09-20 12 views
103

Chúng tôi đặt tất cả các bài kiểm tra đơn vị của mình vào các dự án của riêng họ. Chúng tôi thấy rằng chúng tôi phải làm cho một số lớp học công khai thay vì nội bộ chỉ cho các bài kiểm tra đơn vị. Có anyway để tránh phải làm điều này. Ý nghĩa của bộ nhớ bằng cách làm cho các lớp công khai thay vì niêm phong là gì?Làm mã nội bộ nhưng có sẵn để thử nghiệm đơn vị từ các dự án khác

+2

thể bản sao của mô-đun truy cập [C# "nội bộ" khốc liệt khi thực hiện thử nghiệm đơn vị] (http://stackoverflow.com/questions/358196/c-sharp-internal-access-modifier-when-doing-unit-testing) –

Trả lời

160

Nếu bạn đang sử dụng .NET, thuộc tính assembly InternalsVisibleTo cho phép bạn tạo các hội đồng "bạn bè". Đây là những assembly được đặt tên cụ thể được cho phép truy cập vào các lớp bên trong và các thành viên của assembly khác.

Lưu ý, điều này nên được sử dụng theo quyết định vì nó kết hợp chặt chẽ các hội đồng liên quan. Một sử dụng phổ biến cho InternalsVisibleTo là cho các dự án thử nghiệm đơn vị. Nó có lẽ không phải là một lựa chọn tốt để sử dụng trong các hội đồng ứng dụng thực tế của bạn, vì lý do nêu trên.

+0

Cảm ơn! Điều này chắc chắn sẽ là câu trả lời được chấp nhận. – fresskoma

+14

Tôi muốn đề xuất đặt # DE DEBUG quanh thuộc tính và sau đó thử nghiệm đơn vị trong gỡ lỗi. Bằng cách đó bạn sẽ chắc chắn rằng thuộc tính isnt được đặt trên mã phát hành. –

+0

Đây chỉ là một ý tưởng, tôi không biết .... Làm thế nào về: # nếu DEBUG public class IniReader #else lớp nội IniReader #endif Có lẽ không nên? Tại sao? – jmelhus

-2

Các lớp học có thể vừa công khai vừa bị niêm phong.

Nhưng đừng làm vậy.

Bạn có thể tạo công cụ để phản ánh các lớp bên trong và phát ra một lớp mới truy cập mọi thứ qua phản ánh. MSTest làm điều đó.

Chỉnh sửa: Ý tôi là, nếu bạn không muốn bao gồm các công cụ thử nghiệm trong hội đồng ban đầu của mình; điều này cũng hoạt động nếu các thành viên là riêng tư.

+1

Chờ đã, cái gì? Bạn đang nói không tạo ra một 'lớp niêm phong công khai'? Lý do của bạn cho đá quý đó là gì? – crush

5

Nếu đó là lớp nội bộ thì không được sử dụng riêng biệt. Vì vậy, bạn không nên thực sự thử nghiệm nó ngoài việc kiểm tra một số lớp khác sử dụng đối tượng đó trong nội bộ.

Cũng giống như bạn không nên kiểm tra các thành viên riêng của một lớp, bạn không nên thử nghiệm các lớp nội bộ của một DLL. Các lớp đó là các chi tiết thực hiện của một số lớp có thể truy cập công khai và do đó cần được thực hiện tốt thông qua các bài kiểm tra đơn vị khác.

Ý tưởng là bạn chỉ muốn kiểm tra hành vi của một lớp vì nếu bạn kiểm tra chi tiết triển khai bên trong thì thử nghiệm của bạn sẽ dễ vỡ. Bạn sẽ có thể thay đổi các chi tiết thực hiện của bất kỳ lớp nào mà không vi phạm tất cả các bài kiểm tra của bạn.

Nếu bạn thấy rằng bạn thực sự cần phải kiểm tra lớp đó, sau đó bạn có thể muốn xem xét lại lý do tại sao lớp đó là nội bộ ngay từ đầu.

+0

Chi tiết triển khai phải được thực hiện như là một phần của bài kiểm tra bao gồm. Không nhìn trộm các biến riêng tư ... kiểm tra hành vi mong đợi. Nếu thử nghiệm là đúng .. tất cả các hệ thống ống nước nội bộ và hệ thống dây điện nên được kiểm tra như là một phần của nó. Đã bỏ phiếu. – Gishu

+61

tôi không nhất thiết phải đồng ý với điều này vì các lớp này là "công khai" cho các lớp khác bên trong DLL và chức năng của lớp nên được kiểm tra indepdently – leora

+22

Tôi cũng không đồng ý. Các đơn vị là các đơn vị và chúng phải được thử nghiệm một cách độc lập. – Sentinel

3

cho các mục đích tài liệu

cách khác bạn có thể nhanh chóng lớp nội bằng cách sử dụng Type.GetType phương pháp

dụ

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly; 
//Namespace.ServiceWrapper is internal 
var type = asm.GetType("Namespace.ServiceWrapper"); 
return (IServiceWrapper<T>)Activator 
    .CreateInstance(type, new object[1] { /*constructor parameter*/ }); 

cho loại generic có quá trình khác nhau như dưới đây:

var asm = typeof(IServiceWrapper).Assembly; 
//note the name Namespace.ServiceWrapper`1 
//this is for calling Namespace.ServiceWrapper<> 
var type = asm.GetType("Namespace.ServiceWrapper`1"); 
var genType = type.MakeGenericType(new Type[1] { typeof(T) }); 
return (IServiceWrapper<T>)Activator 
    .CreateInstance(genType, new object[1] { /*constructor parameter*/});