test mac intellij dottrace dotmemory c# unit-testing architecture code-coverage dotcover

c# - mac - Cómo probar o excluir el código privado inaccesible de la cobertura del código



resharper mac (10)

El "problema" al que se refiere es intrínseco al cambio ... caso y, por lo tanto, lo mejor que puede hacer para evitarlo es confiar en una alternativa diferente. Personalmente no me gusta mucho el interruptor ... caso debido a su inflexibilidad. Por lo tanto, esta respuesta tiene la intención de proporcionar una alternativa a las declaraciones de conmutación capaces de ofrecer el tipo de incertidumbre cero que está buscando.

En este tipo de situaciones, por lo general confío en un conjunto de condiciones tomadas de una matriz rellenada al inicio de la función que filtrará automáticamente cualquier entrada no contabilizada. Ejemplo:

private void DoStuffMyWay(StuffToDo stuff) { StuffToDo[] accountedStuff = new StuffToDo[] { StuffToDo.Bike, StuffToDo.Run, StuffToDo.Swim }; if (accountedStuff.Contains(stuff)) { if (stuff == accountedStuff[0]) { //do bike stuff } else if (stuff == accountedStuff[1]) { //do run stuff } else if (stuff == accountedStuff[2]) { //do swim stuff } } }

Esto ciertamente no es demasiado elegante, pero supongo que no soy un programador elegante.

En la medida en que quizás prefiera un tipo diferente de enfoque al problema, aquí tiene otra alternativa; es menos eficiente pero se ve mejor

private void DoStuffWithStyle(StuffToDo stuff) { Dictionary<StuffToDo, Action> accountedStuff = new Dictionary<StuffToDo, Action>(); accountedStuff.Add(StuffToDo.Bike, actionBike); accountedStuff.Add(StuffToDo.Run, actionRun); accountedStuff.Add(StuffToDo.Swim, actionSwim); if (accountedStuff.ContainsKey(stuff)) { accountedStuff[stuff].Invoke(); } } private void actionBike() { //do bike stuff } private void actionRun() { //do run stuff } private void actionSwim() { //do swim stuff }

Con esta segunda opción, incluso podría crear el diccionario como una variable global y rellenarlo junto con la enumeración.

Tengo un montón de ensamblajes con una cobertura de prueba cercana al 100%, pero a menudo me encuentro con una situación como la del ejemplo a continuación. No puedo probar el caso predeterminado del conmutador, que está ahí para protegerse contra futuros errores en los que agrego más elementos a la enumeración, pero me olvido de actualizar la declaración del conmutador para admitir nuevos elementos.

Me gustaría poder encontrar un patrón donde pueda eliminar ese código "no comprobable", probarlo o marcar esa línea de código (pero no todo el método) para ser excluido por el análisis de cobertura.

Puede sonar tonto pero no quiero asumir que el caso predeterminado nunca sucederá, y no quiero agrupar el caso predeterminado con uno que ya existe. Quiero que se genere la excepción cuando cree tales errores. Lo que sucederá tarde o temprano.

Utilizo DotCover para calcular la cobertura en este momento.

Nota: esto es solo un código de ejemplo, pero creo que ilustra un patrón bastante común.

public class Tester { private enum StuffToDo { Swim = 0, Bike, Run } public void DoSomeRandomStuff() { var random = new Random(); DoStuff((StuffToDo)random.Next(3)); } private void DoStuff(StuffToDo stuff) { switch (stuff) { case StuffToDo.Swim: break; case StuffToDo.Bike: break; case StuffToDo.Run: break; default: // How do I test or exclude this line from coverage? throw new ArgumentOutOfRangeException("stuff"); } } }


Es un código inalcanzable, porque el tipo de caso de cambio es Enum, y ha codificado todos los casos posibles. Nunca habrá el caso de ''Predeterminado''.

Puede agregar ''Desconocido'' o algún valor de enumeración adicional que no debería tener una declaración de caso correspondiente, entonces esta advertencia desaparecerá.

private enum StuffToDo { Swim = 0, Bike, Run, // just for code coverage // will never reach here Unknown }

La cobertura de código no profundiza en la evaluación de todas las posibilidades, por lo que no se trata de probarla. Coincide con el patrón de código basado en su comportamiento esperado precodificado

La cobertura de código lo piensa de esta manera, ha agregado todas las posibilidades de enumeración existente, no hay forma de que se llame al valor predeterminado. Así es como se configura su regla, no puede cambiar su regla.


Escriba pruebas que prueben los valores fuera de rango y esperen una excepción.


Estoy de acuerdo con Nate aquí arriba. Ese es probablemente el más fácil (StuffToDo.None). Como alternativa, también podría sobrecargar el método de esta manera:

private void DoStuff(StuffToDo stuff) { DoStuff(stuff.ToString()); } private void DoStuff(string stuff) switch (stuff) { case "Swim": break; case "Bike": break; case "Run": break; default: throw new ArgumentOutOfRangeException("stuff"); } }

Entonces puede probar la situación que cubre en su declaración por defecto.


Los métodos privados son siempre candidatos para ser extraídos en sus propias clases. Especialmente, si tienen lógica compleja, como la tuya. Te sugiero que hagas una clase de StuffDoer con DoStuff() como método público y la DoStuff() en tu clase de Tester . Entonces tiene:

  • Un método DoStuff() , accesible por pruebas
  • Un método DoSomeRandomStuff() que se puede probar con un simulacro en lugar de confiar en el resultado de DoStuff() , lo que lo convierte en una verdadera prueba de unidad.
  • NO SRP-violación.
  • En definitiva, bonito, nítido y código SOLID.

My Resharper también se "queja" sobre el código inalcanzable, que no me gusta durante la programación. Llámame anal, pero me gusta deshacerme del código muerto desde el momento en que lo veo.

Específicamente para su caso, elegiría uno de estos casos como el caso predeterminado y lo pondría en algo como esto:

private void DoStuff(StuffToDo stuff) { switch (stuff) { case StuffToDo.Swim: break; case StuffToDo.Bike: break; case StuffToDo.Run: default: break; } }

Como se esfuerza por lograr una cobertura de prueba del 100%, idealmente, esto debería interrumpirse cuando agrega una entrada de enumeración que debe tratarse de manera diferente.

Y si realmente quiere estar seguro, aún puede uglificarlo:

private void DoStuff(StuffToDo stuff) { switch (stuff) { case StuffToDo.Swim: break; case StuffToDo.Bike: break; case StuffToDo.Run: default: if (stuff != StuffToDo.Run) throw new ArgumentOutOfRangeException("stuff"); break; } }


Puedes usar la directiva pre-compilador usando DEBUG de esta manera (o crea la tuya propia):

#if DEBUG public void DoStuff(StuffToDo stuff) #else private void DoStuff(StuffToDo stuff) #endif { switch (stuff) { case StuffToDo.Swim: break; case StuffToDo.Bike: break; case StuffToDo.Run: break; default: // How do I test or exclude this line from coverage? throw new ArgumentOutOfRangeException("stuff"); } }


Una forma de probar métodos privados en un caso de prueba de unidad es mediante la reflexión, pero creo que podría ser una sobre-matanza en la mayoría de los casos en la que es posible probar con otras formas. En su código, para probar el caso de cambio, lo que puede hacer es, si su método de clase que es público está tomando StuffToDo como un parámetro, entonces necesita escribir varios casos de prueba y cada uno pasa un conjunto diferente de valores para StuffToDo. De esta manera, cuando se ejecuta la instrucción de cambio, puede verificar el comportamiento utilizando su propio método público, pero nuevamente en este caso, asumo que está obteniendo una salida de su método público.

Mirando a su código, otro sentimiento que tengo es que su método público no está tomando ninguna información y no está dando ninguna salida, pero haciendo una modificación que no se ve bien. Parece que está cambiando silenciosamente las cosas que pueden confundir.

Intente tener un método más específico que indique claramente lo que está tomando como entrada y cómo modificarlo.


Actualización: dadas las respuestas aquí, parece que se necesitan algunas aclaraciones importantes.

  1. una enumeración no le impide llegar a la cláusula predeterminada, esta es una forma válida de alcanzarla: DoStuff((StuffToDo)9999)
  2. la cláusula predeterminada no es necesaria en una sentencia de cambio. En ese caso, el código simplemente continuará en silencio después del cambio
  3. Si el método fuera interno, podría usar InternalsToVisible para la prueba de unidad directamente
  4. Podría refactorizar el código de tal manera que las pruebas de unidad puedan alcanzar la estrategia manejada en el interruptor, como @Morten menciona en su respuesta.
  5. Al azar, las fechas y similares son un problema típico de las pruebas unitarias, y lo que hace es cambiar el código de tal manera que puedan reemplazarse durante las pruebas unitarias. Mi respuesta es solo una forma de las muchas maneras de hacerlo. Muestro cómo se puede inyectar a través de un miembro de campo, pero podría ser a través del constructor, y también puede ser una interfaz si se desea.
  6. Si la enumeración no fuera privada, mi ejemplo de código a continuación podría exponer un Func<StuffToDo> , que haría las pruebas un poco más legibles.

Todavía podrías hacer esto y llegar al 100%:

public class Tester { private enum StuffToDo { Swim = 0, Bike, Run } public Func<int> randomStuffProvider = GetRandomStuffInt; public void DoSomeRandomStuff() { DoStuff((StuffToDo)randomStuffProvider()); } private static int GetRandomStuffInt() { //note: don''t do this, you''re supposed to reuse the random instance var random = new Random(); return random.Next(3); } private void DoStuff(StuffToDo stuff) { switch (stuff) { case StuffToDo.Swim: break; case StuffToDo.Bike: break; case StuffToDo.Run: break; default: // How do I test or exclude this line from coverage? throw new ArgumentOutOfRangeException("stuff"); } } }

PD. Sí, expone el RandomStuffProvider para que pueda usarlo en una prueba unitaria. No lo hice funcional Func<StuffToDo> porque eso es privado.


Solo haz esto:

private static int GetRandomStuffInt() { var random = new Random(); DoStuff((StuffToDo)random.Next(Enum.GetNames(typeof(StuffToDo)).Length)); }

Esto asegurará que el número que devuelve sea parte de la enumeración y, por lo tanto, nunca llegará al default: cambiar. ( Link )