2008-10-29 21 views
124

¿Cuál es la mejor manera de probar un método que no devuelve nada? Específicamente en C#.¿Pruebas unitarias de métodos vacíos?

Lo que estoy tratando de probar es un método que toma un archivo de registro y lo analiza para cadenas específicas. Las cadenas se insertan en una base de datos. Nada que no se haya hecho antes pero que sea MUY nuevo para TDD Me pregunto si es posible probar esto o si es algo que realmente no se prueba.

+33

No utilice el término "TDD" si eso no es lo que está haciendo. Estás haciendo pruebas unitarias, no TDD. Si estuvieras haciendo TDD, nunca tendrías una pregunta como "cómo probar un método". La prueba existiría primero, y luego la pregunta sería, "¿cómo pasar esta prueba?" Pero si estuvieras haciendo TDD, tu código se escribiría para la prueba (y no al revés), y esencialmente terminarías respondiendo tu propia pregunta. Su código se formateará de forma diferente como resultado de TDD, y este problema nunca ocurrirá. Solo aclarando. – Suamere

Respuesta

110

Si un método no devuelve nada, o es uno de los siguientes

  • imperativo - O estás pidiendo el objeto de hacer algo para sí .. por ejemplo, cambio de estado (sin esperar confirmación ... se supone que se hará)
  • informativo - simplemente notificando a alguien que algo sucedió (sin esperar respuesta o acción), respectivamente.

Métodos imperativos: puede verificar si la tarea realmente se realizó.Verifique si el cambio de estado realmente tuvo lugar. p.ej.

void DeductFromBalance(dAmount) 

se puede probar mediante la verificación de si el puesto equilibrio este mensaje es de hecho menor que el valor inicial por dAmount

métodos informativos - son raros como miembro de la interfaz pública del objeto ... por lo tanto normalmente no probado por la unidad. Sin embargo, si debe hacerlo, puede verificar si se lleva a cabo el manejo en una notificación. p.ej.

void OnAccountDebit(dAmount) // emails account holder with info 

se puede probar mediante la verificación de si el correo electrónico se envía

Post más detalles acerca de su método real y la gente será capaz de responder mejor.
Actualización: Su método está haciendo 2 cosas. De hecho, lo dividiría en dos métodos que ahora se pueden probar independientemente.

string[] ExamineLogFileForX(string sFileName); 
void InsertStringsIntoDatabase(string[]); 

String [] se puede verificar fácilmente, proporcionando el primer método con un archivo ficticio y el texto que se espera. El segundo es un poco complicado ... puedes usar un Mock (google o search stackoverflow en frameworks burlones) para imitar el DB o golpear el DB real y verificar si las cadenas fueron insertadas en la ubicación correcta. Consulte this thread para obtener algunos buenos libros ... Recomendaría Pragmatic Unit Testing si está en una crisis.
En el código que se utilizaría como

InsertStringsIntoDatabase(ExamineLogFileForX("c:\OMG.log")); 
+0

más detalles arriba – jdiaz

+1

hey gishu, buena respuesta. El ejemplo que dio ... ¿no son más pruebas de integración ...? y si es así, la pregunta sigue siendo, ¿cómo se prueban realmente los Métodos nulos ... tal vez es imposible? – andy

+1

@andy - depende de su definición de 'pruebas de integración'. Los métodos imperativos generalmente cambian de estado, por lo que puede verificarse mediante una prueba unitaria que interroga el estado del objeto. Los métodos informativos se pueden verificar mediante una prueba unitaria que conecta a un oyente/colaborador simulado para garantizar que el sujeto de la prueba emita la notificación correcta. Creo que ambos pueden ser probados razonablemente a través de pruebas unitarias. – Gishu

5

tendrá algún efecto sobre un objeto ... consulta para el resultado del efecto. Si no tiene un efecto visible, ¡no vale la pena probarlo!

-1

También debe comprobar si el método arroja una excepción.

+5

Esto es bastante básico para una respuesta. Tal vez un comentario en su lugar? –

2

Depende de lo que está haciendo. Si tiene parámetros, pase los simulacros que podría preguntar más adelante si han sido llamados con el conjunto correcto de parámetros.

+0

De acuerdo: verificar el comportamiento de los simulacros que prueban el método sería de una sola manera. –

22

Como siempre: prueba lo que se supone que debe hacer el método.

¿Debería cambiar el estado global (uuh, olor a código) en alguna parte?

¿Debería llamar a una interfaz?

¿Debería lanzar una excepción cuando se le llamó con los parámetros incorrectos?

¿No debería arrojar una excepción cuando se le llama con los parámetros correctos?

¿Debería ...?

4

¿Presumiblemente el método hace algo y no simplemente regresa?

Asumiendo que este es el caso, entonces:

  1. Si se modifica el estado del que es objeto propietario, entonces usted debe comprobar que el estado ha cambiado correctamente.
  2. Si toma algún objeto como parámetro y lo modifica, entonces debe probar que el objeto se modificó correctamente.
  3. Si arroja excepciones son ciertos casos, pruebe que esas excepciones se arrojan correctamente.
  4. Si su comportamiento varía según el estado de su propio objeto u otro objeto, preestablezca el estado y pruebe que el método tiene el valor correcto mediante uno de los tres métodos de prueba anteriores).

Si nos dices qué hace el método, podría ser más específico.

45

prueba sus efectos secundarios. Esto incluye:

  • ¿Hay alguna excepción? (Si es así, verifique que sí. Si no es así, intente algunos casos de esquina que podrían no ser cuidadosos, ya que los argumentos nulos son lo más obvio).
  • ¿Funciona bien con sus parámetros? (Si son mutables, ¿los muta cuando no deberían y viceversa?)
  • ¿Tiene el efecto correcto sobre el estado del objeto/tipo al que lo está llamando?

Por supuesto, hay un límite a la cantidad se puede probar. Por lo general, no puede realizar pruebas con todas las entradas posibles, por ejemplo. Realice una prueba pragmática: suficiente para darle la confianza de que su código se diseñó de manera adecuada e implementada correctamente, y lo suficiente como para actuar como documentación complementaria de lo que una persona podría esperar.

3

Use Rhino Mocks para establecer qué llamadas, acciones y excepciones se pueden esperar. Asumiendo que puede burlarse o eliminar partes de su método. Difícil de saber sin conocer algunos detalles aquí sobre el método, o incluso el contexto.

+1

La respuesta a la prueba unitaria nunca debería ser obtener una herramienta de terceros que lo haga por usted. Sin embargo, cuando una persona sabe cómo realizar pruebas unitarias, es aceptable usar una herramienta de terceros para hacerlo más fácil. – Suamere

5

Void return types/Subroutines son noticias viejas. No he hecho un tipo de devolución de Vacío (a menos que estuviese siendo extremadamente flojo) en 8 años (desde el momento de esta respuesta, tan solo un poco antes de que se hiciera esta pregunta).

En lugar de un método como:

public void SendEmailToCustomer() 

Hacer un método que sigue int de Microsoft.TryParse() paradigma:

public bool TrySendEmailToCustomer() 

Tal vez no hay ninguna información de su método necesita para volver para su uso en el largo plazo, pero volviendo al estado del método después de que realiza su trabajo es una gran utilidad para el llamador.

Además, bool no es el único tipo de estado. Hay una serie de veces en que una Subrutina previamente creada podría devolver tres o más estados diferentes (Bueno, Normal, Malo, etc.). En esos casos, usted sólo tiene que utilizar

public StateEnum TrySendEmailToCustomer() 

Sin embargo, mientras que el Try-Paradigma un tanto responde a esta pregunta en la forma de probar un retorno vacío, hay otras consideraciones también. Por ejemplo, durante/después de un ciclo de "TDD", estaría "Refactorizando" y notará que está haciendo dos cosas con su método ... rompiendo así el "Principio de Responsabilidad Individual". Así que eso debería ser atendido primero. En segundo lugar, es posible que haya idenetificado una dependencia ... está tocando Datos "persistentes".

Si está haciendo las cosas de acceso a datos en el método en cuestión, necesita refactorizar en una arquitectura n-tier'd o n-layer'd. Pero podemos suponer que cuando dice "Las cadenas se insertan en una base de datos", en realidad quiere decir que está llamando a una capa de lógica de negocios o algo así. Ya, asumiremos eso.

Cuando se crea una instancia de su objeto, ahora comprende que su objeto tiene dependencias. Aquí es cuando necesita decidir si va a hacer Inyección de Dependencia en el Objeto, o en el Método. Esto significa que su constructor o el método en cuestión necesita un nuevo parámetro:

public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert) 

Ahora que se puede aceptar una interfaz de su objeto/capa de datos de negocios, puede burlarse de ella durante las pruebas unitarias y no tienen dependencias o miedo a las pruebas de integración "Accidental".

Por lo tanto, en su código de transmisión, pasa un objeto REAL IBusinessDataEtc. Pero en tu Prueba de Unidad, pasas en un objeto MOCK IBusinessDataEtc. En ese simulacro, puede incluir propiedades que no sean de interfaz como int XMethodWasCalledCount o algo cuyo estado se actualice cuando se invocan los métodos de interfaz.

Por lo tanto, su Prueba Unitaria pasará por su (s) Método (s) -In-Question, ejecutará cualquier lógica que tengan, y llamará a uno o dos, o un conjunto seleccionado de métodos en su objeto IBusinessDataEtc. Cuando haces tus Afirmaciones al final de tu Prueba de Unidad tienes un par de cosas para probar ahora.

  1. El estado de la "Subrutina" que ahora es un método Try-Paradigm.
  2. El estado de su Mock IBusinessDataEtc objeto.

Para obtener más información sobre las ideas de inyección de dependencias en el nivel de construcción ... en lo que respecta a las pruebas de la unidad ... consulte los patrones de diseño del generador. Agrega una interfaz y clase más para cada interfaz/clase actual que tiene, pero son muy pequeñas y brindan GRANDES aumentos de funcionalidad para una mejor Prueba de Unidad.

2

Prueba esto:

[TestMethod] 
public void TestSomething() 
{ 
    try 
    { 
     YourMethodCall(); 
     Assert.IsTrue(true); 
    } 
    catch { 
     Assert.IsTrue(false); 
    } 
} 
+1

Esto no debería ser necesario, pero se puede hacer como tal –

+0

¡Bienvenido a StackOverflow! Considere agregar alguna explicación a su código. Gracias. – Aurasphere

+1

El ['ExpectedAttribute'] (https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute.aspx) está diseñado para hacer esta prueba más claramente. –

-1

Cualquiera que sea ejemplo que utiliza para llamar al método vacío, Usted sólo puede usar, Verfiy

Por ejemplo:

En mi caso su _Log es el instancia y LogMessage es el método que se probará:

try 
{ 
    this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure"); 
} 
Catch 
{ 
    Assert.IsFalse(ex is Moq.MockException); 
} 

¿El Verify arroja una excepción debido a la falla del método en el que la prueba fallaría?