Hacer el código interno pero disponible para pruebas unitarias de otros proyectos

129

Ponemos todas nuestras pruebas unitarias en sus propios proyectos. Encontramos 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?

leora
fuente
2
posible duplicado del modificador de acceso "interno"
Dour High Arch

Respuestas:

205

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

Tenga en cuenta que esto se debe usar con discreción, ya que acopla firmemente los ensambles involucrados. Un uso común de InternalsVisibleTo es para proyectos de pruebas unitarias. Probablemente no sea una buena opción para usar en los conjuntos de aplicaciones reales, por la razón indicada anteriormente.

Ejemplo:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{
Ceniza
fuente
23
Sugeriría poner un #if DEBUG alrededor del atributo y luego probar la unidad en depuración. De esa manera, se asegurará de que el atributo no esté establecido en el código de lanzamiento.
Steve Cook
Esto es solo una idea, no lo sé ... ¿Qué tal: #if DEBUG clase pública IniReader #else clase interna IniReader #endif Probablemente no recomendado? ¿Por qué?
jmelhus
44
Bueno, ¿por qué limitar las pruebas para depurar compilaciones?
Marco Mp
2
Además, como un simple truco, no es necesario que las asambleas de "amigos" tengan un fuerte nombre (podría ser una buena práctica, incluso si mi gusto personal dice lo contrario, pero no es obligatorio).
Marco Mp
9
Discrepar. Si estoy construyendo un componente complejo con una API pública muy delgada, no es práctico ni realista probar solo a través de la API pública. Terminarás con una bola de barro inmanejable. En cambio, definiría cuidadosamente las unidades internas y las probaría por separado. Como Jeremy D. Miller ha dicho con bastante frecuencia: "Prueba pequeño antes de probar grande".
Dennis Doomen
6

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 utilice ese objeto internamente.

Del mismo modo que no debería probar los miembros privados de una clase, no debería probar las clases internas de una DLL. Esas clases son detalles de implementación de alguna clase de acceso público y, por lo tanto, deben ejercerse bien 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 implementación internos, sus pruebas serán frágiles. Debería poder cambiar los detalles de implementación de cualquier clase sin romper todas sus pruebas.

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

Josh
fuente
2
Los detalles de implementación deben ejercerse como parte de una prueba exhaustiva. No eche un vistazo a las variables privadas ... pruebe el comportamiento esperado. Si la prueba es correcta ... toda la tubería interna y el cableado deben probarse como parte de ella. Votado
Gishu
69
yo no estoy necesariamente de acuerdo con esto ya que estas clases son "público" a otras clases dentro de la DLL y la funcionalidad de la clase debe ser probado indepdently
Leora
27
Tampoco estoy de acuerdo. Las unidades son unidades y deben probarse de forma aislada.
Centinela
55
Gran pauta general, pero no seamos dogmáticos. Las pruebas tienen dos propósitos principales: 1) Regresión: asegúrese de no romper algo, 2) Aumentar la velocidad de desarrollo. Si tengo que soportar un gran servicio cada vez que quiero escribir una línea de código, estaré obstaculizando el desarrollo. Si tengo una pieza interna compleja, quiero poder desarrollar y probar de forma aislada. Por el contrario, no quiero que todo se pruebe de forma aislada (disminuyendo así el valor de la prueba de regresión o duplicando el código de prueba), pero algunos códigos justifican el aislamiento. Quizás la clave es hacer un ensamblaje separado.
Lee Jensen el
2
OP aquí es correcto. Estás acoplando tus pruebas a la implementación interna. Por lo tanto, su equipo es siempre un esclavo para reparar las pruebas y el código de burla horrible. Pruebe solo las API públicas, ya sea la API de lib empaquetada o las API expuestas en red. Todo el código debe ser ejercitable a través de la puerta principal. La implementación de pruebas es la razón por la cual TDD ha muerto en gran medida, es solo una gran PITA para probar literalmente cada clase de la aplicación completa, en lugar de centrarse en garantizar los comportamientos del sistema. Creo que casi todos, incluidos los libros "autorizados", se han equivocado o no lo han dejado claro.
Luke Puplett
4

para fines de documentación

Alternativamente, puede crear una instancia de clase interna mediante el Type.GetTypemé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 procesos como se muestra a continuación:

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
fuente
-5

Las clases pueden ser públicas y selladas.

Pero no hagas eso.

Puede crear una herramienta para reflexionar sobre 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 quieres incluir -cualquier- cosa de prueba en tu ensamblaje original; Esto también funciona si los miembros son privados.

TraumaPony
fuente
1
¿Esperar lo? ¿Estás diciendo que no hagas un public sealed class? ¿Cuál es tu razonamiento para esa gema?
enamorado
@crush traumapony no ha iniciado sesión desde 2009.
Prof. Falken