Modificador de acceso "interno" de C # al realizar pruebas unitarias


469

Soy nuevo en pruebas unitarias y estoy tratando de averiguar si debo comenzar a usar más modificadores de acceso 'internos'. Sé que si usamos 'interno' y configuramos la variable de ensamblaje 'InternalsVisibleTo', podemos probar funciones que no queremos declarar públicas desde el proyecto de prueba. Esto me hace pensar que siempre debería usar 'interno' porque al menos cada proyecto (¿debería?) Tiene su propio proyecto de prueba. ¿Pueden decirme una razón por la que no debería hacer esto? ¿Cuándo debo usar 'privado'?


1
Vale la pena mencionar: a menudo puede evitar la necesidad de que la unidad pruebe sus métodos internos al usarlos System.Diagnostics.Debug.Assert()dentro de los mismos métodos.
Mike Marynowski

Respuestas:


1195

Las clases internas deben probarse y hay un atributo de ensamblaje:

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("MyTests")]

Agregue esto al archivo de información del proyecto, por ejemplo Properties\AssemblyInfo.cs.


70
Agréguelo al proyecto bajo prueba (por ejemplo, en Propiedades \ AssemblyInfo.cs). "MyTests" sería el ensamblaje de prueba.
EricSchaefer

86
Esta realmente debería ser la respuesta aceptada. No sé ustedes, pero cuando las pruebas están "demasiado lejos" del código que están probando, tiendo a ponerme nervioso. Estoy dispuesto a evitar probar cualquier cosa marcada como private, pero demasiadas privatecosas podrían apuntar a una internalclase que está luchando por ser extraída. TDD o no TDD, prefiero tener más pruebas que prueben mucho código, que tener pocas pruebas que ejerzan la misma cantidad de código. Y evitar probar internalcosas no ayuda exactamente a lograr una buena relación.
sm

77
Hay una gran discusión entre @DerickBailey y Dan Tao sobre la diferencia semántica entre lo interno y lo privado y la necesidad de probar los componentes internos . Bien vale la pena leerlo.
Kris McGinnes

31
Al envolver y #if DEBUG, el #endifbloque habilitará esta opción solo en las compilaciones de depuración.
El verdadero Edward Cullen

17
Esta es la respuesta correcta. Cualquier respuesta que diga que solo los métodos públicos deben ser probados en unidades está perdiendo el punto de las pruebas unitarias y es una excusa. Las pruebas funcionales están orientadas a cajas negras. Las pruebas unitarias están orientadas a cajas blancas. Deben probar "unidades" de funcionalidad, no solo API públicas.
Gunnar

127

Si desea probar métodos privados, eche un vistazo a PrivateObjecty PrivateTypeen el Microsoft.VisualStudio.TestTools.UnitTestingespacio de nombres. Ofrecen envoltorios fáciles de usar alrededor del código de reflexión necesario.

Documentos: PrivateType , PrivateObject

Para VS2017 y 2019, puede encontrarlos descargando el MSTest.TestFramework nuget


15
Al votar abajo, por favor deje un comentario. Gracias.
Brian Rasmussen

35
Es estúpido rechazar esta respuesta. Apunta a una nueva solución y una muy buena no mencionada anteriormente.
Ignacio Soler Garcia

Aparentemente, hay algún problema con el uso del TestFramework para la aplicación dirigida a .net2.0 o más reciente: github.com/Microsoft/testfx/issues/366
Johnny Wu

41

Agregando a la respuesta de Eric, también puede configurar esto en el csprojarchivo:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>MyTests</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

O si tiene un proyecto de prueba por proyecto para probar, puede hacer algo como esto en su Directory.Build.propsarchivo:

<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
      <_Parameter1>$(MSBuildProjectName).Test</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Ver: https://stackoverflow.com/a/49978185/1678053
Ejemplo: https://github.com/gldraphael/evlog/blob/master/Directory.Build.props#L5-L12


2
Esta debería ser la mejor respuesta de la OMI. Todas las otras respuestas están muy desactualizadas ya que .net se aleja de la información del ensamblaje y mueve la funcionalidad a las definiciones de csproj.
mBrice1024

12

Sigue usando privado por defecto. Si un miembro no debería exponerse más allá de ese tipo, no debería exponerse más allá de ese tipo, incluso dentro del mismo proyecto. Esto mantiene las cosas más seguras y ordenadas: cuando usa el objeto, queda más claro qué métodos debe usar.

Dicho esto, a veces creo que es razonable hacer que los métodos naturalmente privados sean internos para fines de prueba. Prefiero eso a usar la reflexión, que es refactorizadora-hostil.

Una cosa a considerar podría ser un sufijo "ForTest":

internal void DoThisForTest(string name)
{
    DoThis(name);
}

private void DoThis(string name)
{
    // Real implementation
}

Luego, cuando usa la clase dentro del mismo proyecto, es obvio (ahora y en el futuro) que realmente no debería estar usando este método, solo está allí para fines de prueba. Esto es un poco hacky, y no es algo que yo haga, pero al menos vale la pena considerarlo.


2
Si el método es interno, ¿esto no excluye su uso del conjunto de prueba?
Ralph Shillington

77
De vez en cuando uso el ForTestenfoque, pero siempre lo encuentro feo (agregando código que no proporciona un valor real en términos de lógica comercial de producción). Por lo general, encuentro que tuve que usar el enfoque porque el diseño es algo desafortunado (es decir, tener que restablecer instancias
únicas

1
Tentado a desestimar esto, ¿cuál es la diferencia entre este truco y simplemente hacer que la clase sea interna en lugar de privada? Bueno, al menos con compilaciones condicionales. Entonces se pone muy desordenado.
Bloque CAD el

66
@CADbloke: ¿Te refieres a hacer que el método sea interno en lugar de privado? La diferencia es que es obvio que realmente quieres que sea privado. Cualquier código dentro de su base de código de producción que llame a un método obviamenteForTest es incorrecto, mientras que si solo hace que el método sea interno, parece que está bien usarlo.
Jon Skeet el

2
@CADbloke: puede excluir métodos individuales dentro de una compilación de lanzamiento tan fácilmente en el mismo archivo como usar clases parciales, IMO. Y si lo hace, sugiere que no está ejecutando sus pruebas contra su versión de lanzamiento, lo que me parece una mala idea.
Jon Skeet el

11

También puede usar privado y puede llamar a métodos privados con reflexión. Si está utilizando Visual Studio Team Suite, tiene una buena funcionalidad que generará un proxy para llamar a sus métodos privados por usted. Aquí hay un artículo de proyecto de código que muestra cómo puede hacer el trabajo usted mismo para probar métodos privados y protegidos:

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

En términos de qué modificador de acceso debe usar, mi regla general es comenzar con privado y escalar según sea necesario. De esa manera, expondrá los detalles internos de su clase tan pocos como sean realmente necesarios y ayudará a mantener ocultos los detalles de implementación, como deberían ser.


3

Estoy usando Dotnet 3.1.101y las .csprojadiciones que funcionaron para mí fueron:

<PropertyGroup>
  <!-- Explicitly generate Assembly Info -->
  <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
</PropertyGroup>

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
  <_Parameter1>MyProject.Tests</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

Espero que esto ayude a alguien por ahí!


1
La adición de la generación explícita de información de ensamblaje fue lo que finalmente hizo que funcionara también para mí. ¡Gracias por publicar esto!
thevioletsaber
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.