2008-09-20 6 views
103

Ponemos todas nuestras pruebas unitarias en sus propios proyectos. Descubrimos que tenemos que hacer ciertas clases públicas en lugar de internas solo para las pruebas unitarias. ¿Hay alguna forma de evitar tener que hacer esto? ¿Cuáles son las implicaciones de la memoria al hacer que las clases sean públicas en lugar de selladas?Hacer código interno pero disponible para pruebas unitarias de otros proyectos

+2

posible duplicado de [C# modificador de acceso "interno" fier al hacer pruebas unitarias] (http://stackoverflow.com/questions/358196/c-sharp-internal-access-modifier-when-doing-unit-testing) –

Respuesta

160

Si está utilizando .NET, el atributo de ensamblaje InternalsVisibleTo le permite crear conjuntos "amigos". Estos son ensamblajes específicos con un nombre fuerte que pueden acceder a las clases internas y a los miembros del otro ensamblado.

Nota, esto se debe utilizar con discreción, ya que combina estrechamente los conjuntos involucrados. Un uso común para InternalsVisibleTo es para proyectos de pruebas unitarias. Probablemente no sea una buena opción para el uso en los ensamblajes de aplicaciones reales, por la razón indicada anteriormente.

+0

¡Gracias! Esta definitivamente debería ser la respuesta aceptada. – fresskoma

+14

Sugiero poner #if DEBUG alrededor del atributo, y luego pruebas unitarias en depuración. De esa forma, estarás seguro de que el atributo no está establecido en el código de lanzamiento. –

+0

Esto es sólo una idea, no sé .... ¿Qué tal: #if DEBUG clase pública IniReader #else clases interna IniReader #endif Probablemente no se recomienda? ¿Por qué? – jmelhus

-2

Las clases pueden ser públicas Y selladas.

Pero, no hagas eso.

Puede crear una herramienta para reflejar las clases internas y emitir una nueva clase que acceda a todo a través de la reflexión. MSTest hace eso.

Editar: Quiero decir, si no desea incluir -muchas cosas de prueba en su ensamblaje original; esto también funciona si los miembros son privados.

+1

Espera, ¿qué? ¿Estás diciendo que no hagas una "clase pública sellada"? ¿Cuál es su razonamiento para esa joya? – crush

5

Si se trata de una clase interna, no se debe utilizar de forma aislada. Por lo tanto, no debería probarlo aparte de probar alguna otra clase que haga uso de ese objeto internamente.

Del mismo modo que no debe probar miembros privados de una clase, no debería probar clases internas de una DLL. Esas clases son detalles de implementación de alguna clase de acceso público, y por lo tanto deben ser ejercidas a través de otras pruebas unitarias.

La idea es que solo desee probar el comportamiento de una clase porque si prueba los detalles de la implementación interna, entonces sus pruebas serán frágiles. Debería poder cambiar los detalles de implementación de cualquier clase sin romper todas sus pruebas.

Si encuentra que realmente necesita probar esa clase, entonces es posible que desee volver a examinar por qué esa clase es interna en primer lugar.

+0

Los detalles de implementación deben ser ejercidos como parte de una prueba abarcadora. No mire las variables privadas ... pruebe el comportamiento esperado. Si la prueba es correcta ... todas las tuberías internas y el cableado deben probarse como parte de ella. Votado arriba. – Gishu

+61

no necesariamente estoy de acuerdo con esto, ya que estas clases son "públicas" para otras clases dentro de la DLL y la funcionalidad de la clase debe probarse indepdentadamente – leora

+22

. Tampoco estoy de acuerdo. Las unidades son unidades y deben probarse de forma aislada. – Sentinel

3

para fines de documentación

, alternativamente, se puede crear una instancia de clase interna mediante el uso de Type.GetType método

ejemplo

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

para el tipo genérico hay diferentes proceso como bramido:

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