Udostępnianie kodu jako wewnętrznego, ale dostępnego do testowania jednostkowego z innych projektów

131

Wszystkie nasze testy jednostkowe umieszczamy we własnych projektach. Odkrywamy, że musimy upublicznić niektóre klasy zamiast wewnętrznych tylko dla testów jednostkowych. Czy w ogóle można tego uniknąć. Jaki jest wpływ pamięci na upublicznianie klas zamiast zapieczętowanych?

leora
źródło

Odpowiedzi:

208

Jeśli używasz platformy .NET, atrybut zestawu InternalsVisibleTo umożliwia tworzenie zestawów „zaprzyjaźnionych”. Są to specyficzne silnie nazwane zestawy, które mają dostęp do klas wewnętrznych i elementów członkowskich innego zestawu.

Należy pamiętać, że należy to stosować z rozwagą, ponieważ ściśle łączy zaangażowane zespoły. Typowe zastosowanie InternalsVisibleTo to projekty testów jednostkowych. Prawdopodobnie nie jest to dobry wybór do użycia w rzeczywistych zestawach aplikacji z podanego powyżej powodu.

Przykład:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{
Popiół
źródło
23
Sugerowałbym umieszczenie #if DEBUG wokół atrybutu, a następnie testy jednostkowe w debugowaniu. W ten sposób będziesz mieć pewność, że atrybut nie jest ustawiony w kodzie wydania.
Steve Cook
To tylko pomysł, nie wiem… Co powiesz na: #if DEBUG klasa publiczna IniReader #else klasa wewnętrzna IniReader #endif Prawdopodobnie niezalecane? Czemu?
jmelhus,
4
Cóż, po co ograniczać testy do debugowania kompilacji?
Marco Mp
2
Poza tym, po prostu szczypta, nie ma potrzeby, aby zgromadzenia „przyjaciół” były silnie nazywane (może to być dobra praktyka - nawet jeśli mój osobisty gust mówi inaczej, ale nie jest to obowiązkowe).
Marco Mp
9
Nie zgadzać się. Jeśli tworzę złożony komponent z bardzo cienkim publicznym interfejsem API, testowanie tylko za pośrednictwem publicznego interfejsu API jest niepraktyczne i nierealne. Skończysz z niemożliwą do utrzymania kulą błota. Zamiast tego dokładnie zdefiniowałbym jednostki wewnętrzne i przetestowałbym je oddzielnie. Jak często mawiał Jeremy D. Miller: „Testuj mały, zanim przetestujesz duży”.
Dennis Doomen
6

Jeśli jest to klasa wewnętrzna, nie może być używana w izolacji. Dlatego tak naprawdę nie powinieneś testować tego poza testowaniem innej klasy, która korzysta z tego obiektu wewnętrznie.

Tak jak nie powinieneś testować prywatnych członków klasy, nie powinieneś testować wewnętrznych klas biblioteki DLL. Te klasy są szczegółami implementacji niektórych publicznie dostępnych klas i dlatego powinny być dobrze ćwiczone przez inne testy jednostkowe.

Chodzi o to, że chcesz tylko przetestować zachowanie klasy, ponieważ jeśli przetestujesz szczegóły implementacji wewnętrznej, testy będą kruche. Powinieneś być w stanie zmienić szczegóły implementacji dowolnej klasy bez przerywania wszystkich testów.

Jeśli okaże się, że naprawdę potrzebujesz przetestować tę klasę, możesz w pierwszej kolejności ponownie zbadać, dlaczego ta klasa jest wewnętrzna.

Josh
źródło
2
Szczegóły implementacji powinny być przećwiczone jako część całościowego testu. Nie zaglądaj do prywatnych zmiennych ... testuj oczekiwane zachowanie. Jeśli test wypadnie pomyślnie… cała wewnętrzna instalacja wodno-kanalizacyjna i okablowanie powinny być przetestowane jako część tego testu. Zagłosowano.
Gishu
70
Niekoniecznie się z tym zgadzam, ponieważ te klasy są „publiczne” dla innych klas wewnątrz biblioteki DLL, a funkcjonalność klasy powinna zostać przetestowana
niezależnie
28
Ja też się nie zgadzam. Jednostki są jednostkami i muszą być testowane oddzielnie.
Sentinel
5
Świetna ogólna wskazówka, ale nie dogmatyzujmy. Testy służą dwóm głównym celom: 1) Regresja - upewnij się, że czegoś nie złamałeś, 2) Zwiększenie szybkości rozwoju. Jeśli za każdym razem, gdy chcę napisać linię kodu, będę musiał stawić czoła ogromnej usłudze, utrudnię rozwój. Jeśli mam złożony element wewnętrzny, chcę mieć możliwość rozwijania i testowania w izolacji. I odwrotnie, nie chcę, aby wszystko było testowane osobno (zmniejszając w ten sposób wartość testu regresji lub powielając kod testu), ale jakiś kod uzasadnia izolację. Być może kluczem jest uczynienie z tego oddzielnego zespołu.
Lee Jensen,
2
OP tutaj jest poprawne. Łączysz testy z wewnętrzną implementacją. Dlatego twój zespół jest na zawsze niewolnikiem poprawiania testów i okropnego szyderczego kodu. Testuj tylko publiczne interfejsy API, czy to spakowane lib API czy interfejsy API dostępne w sieci. Cały kod powinien być możliwy do wykonania przez drzwi frontowe. Testowanie implementacji jest powodem, dla którego TDD w dużej mierze umarło, to po prostu ogromne PITA, aby przetestować dosłownie każdą klasę całej aplikacji, zamiast skupiać się na zapewnieniu zachowania systemu. Myślę, że prawie wszyscy, w tym książki „autorytatywne”, źle to zrobili lub nie wyjaśnili tego jasno.
Luke Puplett
4

do celów dokumentacyjnych

alternatywnie możesz utworzyć instancję klasy wewnętrznej przy użyciu Type.GetTypemetody method

przykład

//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*/ });

dla typu ogólnego istnieją różne procesy, jak poniżej:

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*/});
ktutnik
źródło
-5

Klasy mogą być zarówno publiczne, jak i zapieczętowane.

Ale nie rób tego.

Możesz utworzyć narzędzie do odzwierciedlania klas wewnętrznych i emitować nową klasę, która uzyskuje dostęp do wszystkiego za pośrednictwem odbicia. MSTest to robi.

Edycja: mam na myśli, jeśli nie chcesz włączać -jakie- testowania rzeczy do swojego oryginalnego zestawu; działa to również, jeśli członkowie są prywatni.

TraumaPony
źródło
1
Czekaj, co? Mówisz, że nie rób public sealed class? Jakie masz powody dla tego klejnotu?
zmiażdż
@crush traumapony nie logował się od 2009 roku.
Prof. Falken