Prueba unitaria para nombrar las mejores prácticas [cerrado]

564

¿Cuáles son las mejores prácticas para nombrar clases de prueba unitarias y métodos de prueba?

Esto se discutió en SO antes, en ¿Cuáles son algunas convenciones de nomenclatura populares para las pruebas unitarias?

No sé si este es un enfoque muy bueno, pero actualmente en mis proyectos de prueba, tengo mapeos uno a uno entre cada clase de producción y una clase de prueba, por ejemplo, Producty ProductTest.

En mis clases de prueba, luego tengo métodos con los nombres de los métodos que estoy probando, un guión bajo, y luego la situación y lo que espero que suceda, por ejemplo Save_ShouldThrowExceptionWithNullName().

James Newton-King
fuente
Ver aquí: stackoverflow.com/questions/96297/…
Semicolon olvidado el
1
Esto no responde a su pregunta, pero vale la pena leer: haacked.com/archive/2012/01/02/structuring-unit-tests.aspx
roba
3
La guía de estilo de Google dice: test<MethodUnderTest>_<state>por ejemplo, testPop_emptyStack google-styleguide.googlecode.com/svn/trunk/javaguide.html 5.2.3 Nombres de métodos. En caso de duda, sigue a Google.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
@CiroSantilli 六四 事件 法轮功 包 卓 轩 Y la siguiente oración dice: "No hay una forma correcta de nombrar los métodos de prueba". Imagínate.
user2418306
1
Todavía prefiero la recomendación de Phil Haack incluso años después.
pimbrouwers

Respuestas:

524

Me gusta la estrategia de nombres de Roy Osherove , es la siguiente:

[UnitOfWork_StateUnderTest_ExpectedBehavior]

Tiene toda la información necesaria sobre el nombre del método y de manera estructurada.

La unidad de trabajo puede ser tan pequeña como un solo método, una clase o tan grande como varias clases. Debe representar todas las cosas que se probarán en este caso de prueba y que están bajo control.

Para los ensambles utilizo el .Testsfinal típico , que creo que está bastante extendido y lo mismo para las clases (que termina con Tests):

[NameOfTheClassUnderTestTests]

Anteriormente utilicé Fixture como sufijo en lugar de Pruebas, pero creo que este último es más común, luego cambié la estrategia de nomenclatura.

Marc Climent
fuente
228
Para mí no tiene sentido poner el nombre del método en el método de prueba. ¿Qué pasa si cambias el nombre del método? Ninguna herramienta de refactorización cambiará el nombre de las pruebas por usted. Eventualmente, terminas cambiando el nombre de los métodos de prueba a mano o más probablemente teniendo pruebas mal nombradas. Es como con los comentarios. Mucho es peor que no comentar código en absoluto.
Piotr Perak
80
@ Peri, creo que es una compensación. Por un lado, los nombres de sus exámenes pueden quedar desactualizados, por otro lado, no puede saber qué método está probando su examen. Me parece que este último aparece mucho más a menudo.
Joel McBeth
15
Para agregar al comentario de Peri, todos los métodos son responsables de alguna acción, por ejemplo UpdateManager.Update(). Teniendo esto en cuenta, tiendo a llamar a mis pruebas WhenUpdating_State_Behaviouro WhenUpdating_Behaviour_State. De esta manera, pruebo una acción particular de una clase mientras evito poner un nombre de método en un nombre de prueba. Pero lo más importante es que tengo que tener una idea de qué lógica empresarial está fallando cuando veo el nombre de una prueba que falla
Ramunas
8
Resharper e IntelliJ probablemente encontrarán su método de prueba y le ofrecerán cambiarle el nombre si refactoriza / renombra usando esas herramientas. También intenta buscar en los comentarios donde mencionas el nombre del método y también los actualizas.
Jeff Martin
6262
Los buenos nombres de métodos a menudo son los mismos que la acción que realiza el método. Si tiene que decidir entre nombrar su prueba después de su método o la acción que realiza el método que puede ser una pista, debe cambiar el nombre de su método . (Sin embargo, no en todos los casos)
Kaadzia
121

Me gusta seguir el estándar de nomenclatura "Debería" para las pruebas al nombrar el dispositivo de prueba después de la unidad bajo prueba (es decir, la clase).

Para ilustrar (usando C # y NUnit):

[TestFixture]
public class BankAccountTests
{
  [Test]
  public void Should_Increase_Balance_When_Deposit_Is_Made()
  {
     var bankAccount = new BankAccount();
     bankAccount.Deposit(100);
     Assert.That(bankAccount.Balance, Is.EqualTo(100));
  }
}

¿Por qué "debería" ?

Encuentro que obliga a los escritores de prueba a nombrar la prueba con una oración en la línea de "Debería [estar en algún estado] [después / antes / cuando] [la acción se lleva a cabo]"

Sí, escribir "Debería" en todas partes se vuelve un poco repetitivo, pero como dije obliga a los escritores a pensar de la manera correcta (por lo que puede ser bueno para los principiantes). Además, generalmente da como resultado un nombre de prueba legible en inglés.

Actualización :

Me he dado cuenta de que Jimmy Bogard también es fanático del 'debería' e incluso tiene una biblioteca de pruebas unitarias llamada debería .

Actualización (4 años después ...)

Para aquellos interesados, mi enfoque para nombrar pruebas ha evolucionado a lo largo de los años. Uno de los problemas con el patrón Deber que describo anteriormente es que no es fácil saber de un vistazo qué método se está probando. Para OOP, creo que tiene más sentido comenzar el nombre de la prueba con el método bajo prueba. Para una clase bien diseñada, esto debería dar como resultado nombres de métodos de prueba legibles. Ahora uso un formato similar a <method>_Should<expected>_When<condition>. Obviamente, dependiendo del contexto, es posible que desee sustituir los verbos debería / cuándo por algo más apropiado. Ejemplo: Deposit_ShouldIncreaseBalance_WhenGivenPositiveValue()

Jack Ukleja
fuente
32
Tal vez incluso mejor y menos redundante, acaba de escribir una frase que le dice lo que hace, suponiendo que funciona la prueba: increasesBalanceWhenDepositIsMade().
hotshot309
3
Recientemente vi un artículo que mencionaba una convención de nomenclatura similar (desearía haberla marcado). Diseñado para hacer que las listas de pruebas sean muy legibles cuando se ordenan por dispositivo de prueba. Verá algo como "BankAccount" y luego debajo de él (en diferentes líneas) "Should_Increase_Balance_When_Deposit_Is_Made" "Should_Decrease_Balance_When_Withdrawal_Is_Made", etc.
Simon Tewsi
Encontré el artículo. Está en el blog CodeThinked de Justin Etheredge Comenzando a burlarse de Moq 3 - Parte 1 .
Simon Tewsi
11
También utilizo debería y cuándo pero al revés. por ejemplo, WhenCustomerDoesNotExist_ShouldThrowException (). Para mí, esto tiene mucho más sentido de lo que debería cuando (es decir, en un determinado escenario debería haber un cierto resultado esperado). Esto también encaja con AAA (Organizar, Actuar, Afirmar) ... la Afirmación está al final ... no al principio ;-)
bytedev
2
@Schneider: teniendo en cuenta que "debería" = "recomendado" y luego opcional, me pregunto: ¿no sería mejor usar "will" = "must" que luego obligatorio / obligatorio. Por ejemplo, los RFC hacen la diferencia entre ambos. Entonces, ¿se recomienda o requiere pasar las pruebas?
blackwizard
79

Me gusta este estilo de nombres:

OrdersShouldBeCreated();
OrdersWithNoProductsShouldFail();

y así. Realmente deja claro a un no evaluador cuál es el problema.

Sklivvz
fuente
63
pero @ hotshot309, puede estar usando .NET - Convenciones de capitalización .NET
Ace
2
@Ace, estoy totalmente de acuerdo contigo y noté esto aproximadamente un minuto después de que publiqué este comentario. Juré que lo borré cuando vi mi error, pero de alguna manera, supongo que no. Lo siento por eso.
hotshot309
3
@CoffeeAddict porque los guiones bajos dentro de los identificadores son una <del> aberración </del> no realmente idiomática en C #
Sklivvz
2
También me gusta evitar el uso de shouldlo preferiría willasíOrdersWithNoProductsWillFail()
Calin
44
@Calin En mi opinión, el uso Willno es realmente apropiado y al hacerlo, le estás diciendo al lector por error que de ninguna manera la prueba fallará ... si usas Willpara expresar algo en el futuro que podría no suceder, estás usarlo incorrectamente, mientras que Shouldes la mejor opción aquí porque indica que desea / desea que suceda algo, pero no sucedió o no pudo, cuando se ejecuta la prueba le dice si falló / tuvo éxito, por lo que no puede implicar que de antemano, esa es la explicación lógica, ¿cuál es la tuya? ¿Por qué lo evitas Should?
Eyal Solnik
51

Kent Beck sugiere:

  • Un accesorio de prueba por 'unidad' (clase de su programa). Los accesorios de prueba son clases en sí. El nombre del dispositivo de prueba debe ser:

    [name of your 'unit']Tests
    
  • Los casos de prueba (los métodos de fijación de prueba) tienen nombres como:

    test[feature being tested]
    

Por ejemplo, tener la siguiente clase:

class Person {
    int calculateAge() { ... }

    // other methods and properties
}

Un accesorio de prueba sería:

class PersonTests {

    testAgeCalculationWithNoBirthDate() { ... }

    // or

    testCalculateAge() { ... }
}
Sergio Acosta
fuente
44
Deseo que más personas sigan estas pautas. No hace mucho tiempo tuve que cambiar el nombre de más de 20 métodos de prueba porque tenían nombres como "ATest", "BasicTest" o "ErrorTest".
Wedge
85
¿no se vuelve redundante el prefijo de método de 'prueba' dado el sufijo de la clase?
Gavin Miller
50
Recuerda eso cuando Kent escribió ese libro. Los atributos no fueron inventados. Por lo tanto, el nombre Prueba en el nombre del método indicaba al marco de prueba que el método era una prueba. También han pasado muchas cosas desde 2002.
Thomas Jespersen
14
testCalculateAge ... este es un nombre sin sentido para su método de prueba. "prueba" es redundante (¿nombra todos sus métodos con el prefijo "método"?). El resto del nombre no tiene ninguna condición bajo prueba o lo que se esperaba. CalculateAge es el método que se está probando ..... quien sabe ...?
bytedev
1
Me gustaría agregar que cuando se usa esta estrategia, se requiere documentación para especificar el resultado esperado. Como nota al margen sobre el prefijo 'prueba'; Algunos marcos de pruebas unitarias requieren prefijos o sufijos específicos para reconocer las pruebas. Prefijar clases abstractas con 'Resumen' no se considera redundante (porque es autodocumentado), entonces, ¿por qué no se aplica lo mismo con 'Prueba'?
siebz0r
17

Nombres de clase . Para los nombres de los dispositivos de prueba, encuentro que "Prueba" es bastante común en el lenguaje ubicuo de muchos dominios. Por ejemplo, en un dominio de la ingeniería: StressTesty en un dominio de cosméticos: SkinTest. Lamento no estar de acuerdo con Kent, pero usar "Test" en mis accesorios de prueba ( StressTestTest?) Es confuso.

"Unidad" también se usa mucho en dominios. Por ej MeasurementUnit. ¿Se llama una clase MeasurementUnitTestuna prueba de "Medición" o "Unidad de medida"?

Por eso me gusta usar el prefijo "Qa" para todas mis clases de prueba. Por ejemplo QaSkinTesty QaMeasurementUnit. Nunca se confunde con los objetos de dominio, y el uso de un prefijo en lugar de un sufijo significa que todos los accesorios de prueba viven juntos visualmente (útil si tiene falsificaciones u otras clases de apoyo en su proyecto de prueba)

Espacios de nombres . Trabajo en C # y mantengo mis clases de prueba en el mismo espacio de nombres que la clase que están probando. Es más conveniente que tener espacios de nombres de prueba separados. Por supuesto, las clases de prueba están en un proyecto diferente.

Nombres de métodos de prueba . Me gusta nombrar mis métodos WhenXXX_ExpectYYY. Aclara la condición previa y ayuda con la documentación automatizada (a la TestDox). Esto es similar al consejo del blog de pruebas de Google, pero con una mayor separación de precondiciones y expectativas. Por ejemplo:

WhenDivisorIsNonZero_ExpectDivisionResult
WhenDivisorIsZero_ExpectError
WhenInventoryIsBelowOrderQty_ExpectBackOrder
WhenInventoryIsAboveOrderQty_ExpectReducedInventory
Grundoon
fuente
usted habló sobre nombres de métodos de prueba y nombres de accesorios de prueba. los nombres de los dispositivos de prueba se asignan a clases de producción. ¿Dónde escribe el nombre del método de producción en su prueba?
The Light
12

Yo uso el concepto dado-cuando-entonces . Eche un vistazo a este breve artículo http://cakebaker.42dh.com/2009/05/28/given-when-then/ . El artículo describe este concepto en términos de BDD, pero también puede usarlo en TDD sin ningún cambio.

Pashec
fuente
Given-When-Then es lo mismo que MethodName_Scenario_ExpectedBehavior, ¿no?
The Light
1
No exactamente. Given_When_Then se refiere más a: GivenAnEntity_WhenSomeActionHappens_ThatResultIsExpected La prueba debe expresar la voluntad de probar un comportamiento, no una implementación .
plog17
1
"Dado cuándo entonces" a menudo se conoce como pepinillo. Es un DSL que salió de las herramientas Cucumber, JBehave y Behat.
bytedev
+1 este es el mejor método de la OMI. Desacoplar cómo un método hace algo de lo que espera que sea el resultado es muy poderoso y evita muchos problemas descritos en otros comentarios.
Lee
7

Creo que una de las cosas más importantes es ser coherente en su convención de nomenclatura (y estar de acuerdo con otros miembros de su equipo). Muchas veces veo muchas convenciones diferentes utilizadas en el mismo proyecto.

bytedev
fuente
7

Recientemente se me ocurrió la siguiente convención para nombrar mis pruebas, sus clases y proyectos que contengan para maximizar sus descriptivos:

Digamos que estoy probando la Settingsclase en un proyecto en el MyApp.Serializationespacio de nombres.

Primero crearé un proyecto de prueba con el MyApp.Serialization.Testsespacio de nombres.

Dentro de este proyecto y, por supuesto, el espacio de nombres crearé una clase llamada IfSettings(guardada como IfSettings.cs ).

Digamos que estoy probando el SaveStrings()método. -> Voy a nombrar la prueba CanSaveStrings().

Cuando ejecuto esta prueba, mostrará el siguiente encabezado:

MyApp.Serialization.Tests.IfSettings.CanSaveStrings

Creo que esto me dice muy bien lo que está probando.

Por supuesto, es útil que en inglés el sustantivo "Tests" sea el mismo que el verbo "tests".

No hay límite para su creatividad al nombrar las pruebas, de modo que obtengamos títulos completos de las oraciones para ellas.

Por lo general, los nombres de las pruebas tendrán que comenzar con un verbo.

Ejemplos incluyen:

  • Detecta (p DetectsInvalidUserInput. Ej. )
  • Lanza (por ejemplo ThrowsOnNotFound)
  • Will (por ejemplo WillCloseTheDatabaseAfterTheTransaction)

etc.

Otra opción es usar "eso" en lugar de "si".

Sin embargo, este último me ahorra pulsaciones de teclas y describe más exactamente lo que estoy haciendo, ya que no sé, que el comportamiento probado está presente, pero estoy probando si lo está.

[ Editar ]

Después de usar la convención de nomenclatura anterior durante un poco más de tiempo, descubrí que el prefijo If puede ser confuso cuando se trabaja con interfaces. Sucede que la clase de prueba IfSerializer.cs se parece mucho a la interfaz ISerializer.cs en la "pestaña Abrir archivos". Esto puede ser muy molesto cuando se alterna entre las pruebas, la clase que se está probando y su interfaz. Como resultado, ahora elegiría Eso sobre If como prefijo.

Además, ahora uso, solo para métodos en mis clases de prueba, ya que no se considera la mejor práctica en ningún otro lugar, la "_" para separar palabras en los nombres de mis métodos de prueba como en:

[Test] public void detects_invalid_User_Input()

Encuentro que esto es más fácil de leer.

[ Finalizar edición ]

Espero que esto genere algunas ideas más, ya que considero que nombrar las pruebas es de gran importancia, ya que puede ahorrarle mucho tiempo que de lo contrario se habría gastado tratando de comprender lo que están haciendo las pruebas (por ejemplo, después de reanudar un proyecto después de una pausa prolongada) .

Thorsten Lorenz
fuente
2

En VS + NUnit, generalmente creo carpetas en mi proyecto para agrupar las pruebas funcionales. Luego creo clases de dispositivos de prueba de unidad y les doy el nombre del tipo de funcionalidad que estoy probando. Los métodos [Test] se nombran a lo largo de las líneas de Can_add_user_to_domain:

- MyUnitTestProject   
  + FTPServerTests <- Folder
   + UserManagerTests <- Test Fixture Class
     - Can_add_user_to_domain  <- Test methods
     - Can_delete_user_from_domain
     - Can_reset_password
Kev
fuente
2

Debo agregar que mantener sus pruebas en el mismo paquete, pero en un directorio paralelo a la fuente que se está probando, elimina la hinchazón del código una vez que esté listo para implementarlo sin tener que hacer un montón de patrones de exclusión.

Personalmente, me gustan las mejores prácticas descritas en la "Guía de bolsillo JUnit" ... ¡es difícil superar un libro escrito por el coautor de JUnit!

Steve Moyer
fuente
3
No crea que esto realmente responde la pregunta en cuestión: ¿podría hacer una edición y hacer referencia a JUnit Pocket Guide? ¡Gracias!
Nate-Wilkins
0

el nombre del caso de prueba para la clase Foo debe ser FooTestCase o algo similar (FooIntegrationTestCase o FooAcceptanceTestCase), ya que es un caso de prueba. Consulte http://xunitpatterns.com/ para conocer algunas convenciones de nomenclatura estándar como prueba, caso de prueba, dispositivo de prueba, método de prueba, etc.


fuente