Junit: ejecutar el método de configuración una vez
119
Configuré una clase con un par de pruebas y, en lugar de usar, @Beforeme gustaría tener un método de configuración que se ejecute solo una vez antes de todas las pruebas. ¿Es eso posible con Junit 4.8?
Aunque estoy de acuerdo con @assylias en que usar @BeforeClasses una solución clásica, no siempre es conveniente. El método anotado con @BeforeClassdebe ser estático. Es muy inconveniente para algunas pruebas que necesitan una instancia de caso de prueba. Por ejemplo, pruebas basadas en Spring que usan@Autowired funcionar con servicios definidos en el contexto de Spring.
En este caso, personalmente uso el setUp()método regular anotado con @Beforeanotación y administro mi bandera personalizada static(!) boolean:
privatestaticboolean setUpIsDone =false;.....@Beforepublicvoid setUp(){if(setUpIsDone){return;}// do the setup
setUpIsDone =true;}
Añadiendo al comentario de Kenny Cason sobre por qué debe ser estático. Debe ser estático porque JUnit crea una nueva instancia de la clase de prueba para cada método @Test. La variable de instancia se restablecerá a su valor predeterminado (falso) para cada instancia si no es estática. Consulte para obtener más información: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz
2
Esto funciona, excepto en el caso de que el setUp()método esté en una superclase: he publicado una respuesta a continuación para intentar resolver esto.
Steve Chambers
4
Dudo en decirle esto a alguien con un representante de 84k, pero BeforeClass de hecho no responde a la pregunta: BeforeClass se ejecuta al comienzo de cada clase de prueba. Pero el OP pidió uno que se ejecute "solo una vez antes de todas las pruebas". Su solución propuesta podría hacer esto, pero tendría que hacer que todas sus clases de prueba extendieran una clase "CommonTest" ...
Mike
1
@mikerodent, en mi humilde opinión OP preguntó sobre todas las pruebas dentro de su caso de prueba, no todas las pruebas en general. Entonces, tu comentario es menos relevante. Por cierto, no se preocupe por decirle nada a nadie, incluso si su reputación es alta. Al menos esto es lo que hago :). Y mi reputación era significativamente más baja en agosto de 2012 cuando respondí la pregunta.
AlexR
No funciona para mi caso, las variables inicializadas en la configuración se reinician después de cada prueba, por lo que no tiene sentido iniciar solo una vez.
@mikerodent Entendí la pregunta como "todas las pruebas en la clase", pero tienes razón, puede que no sea lo que quería el OP.
Assylias
29
JUnit 5 ahora tiene una anotación @BeforeAll:
Indica que el método anotado debe ejecutarse antes que todos los métodos @Test en la clase actual o jerarquía de clases; análogo a @BeforeClass de JUnit 4. Tales métodos deben ser estáticos.
¡Las anotaciones del ciclo de vida de JUnit 5 parecen haberlo hecho bien finalmente! Puede adivinar qué anotaciones están disponibles sin siquiera mirar (por ejemplo, @BeforeEach @AfterAll)
Tiene el mismo problema @BeforeClass, tiene que serlo static. La solución de IMO @ AlexR es mejor.
zengr
@zengr tiende a estar de acuerdo contigo: como le he dicho a AlexR, su solución requiere que todas las clases de prueba se subclasifiquen de una clase CommonTest si solo se ejecuta una vez. Pero es tan simple como puede ser, y en mi humilde opinión, probablemente no debería usar una solución "elegante" suministrada por el marco cuando un mecanismo simple está disponible en el lenguaje. A menos que haya una buena razón, por supuesto. Además, usar una cosa simple como la suya, con un buen nombre de tipo "hace lo que dice en la lata", ayuda con la legibilidad.
Mike Rodent
Habiendo dicho esto, nuevamente en mi humilde opinión, parece que hay mucha más justificación para tener una anotación "AfterAll": sería muy difícil e ingenioso idear un mecanismo para detectar cuándo se realizaron todas las pruebas. Por el contrario, por supuesto, los puristas probablemente dirán que nunca debería tener que hacer una "limpieza final", es decir, que cada "tearDown" debería dejar todos los recursos en un estado prístino ... ¡y probablemente tengan razón!
Mike Rodent
¿Funciona esto con Maven donde hay varios módulos, cada uno con sus pruebas?
Mark Boon
@mike rodent, en mi caso, la configuración y eliminación de archivos de prueba en el sistema de archivos antes / después de cada prueba parece llevar a puntos muertos en los archivos. Por ahora, he llegado de forma independiente a la solución de AlexR para configurar una vez. Tengo dos banderas estáticas, ya están configuradas y sucias. setup () llama a cleanup () si inicialmente se detecta un estado sucio o si una falla de configuración conduce a un estado sucio. Para limpiar después de ejecutar las pruebas, las ejecuto nuevamente. Desordenado, para nada ideal, no en nuestro proceso de construcción. Sigo buscando una forma mejor (jUnit 4.12).
Rebeccah
9
Cuando setUp()está en una superclase de la clase de prueba (por ejemplo, a AbstractTestBasecontinuación), la respuesta aceptada se puede modificar de la siguiente manera:
publicabstractclassAbstractTestBase{privatestaticClass<?extendsAbstractTestBase> testClass;.....publicvoid setUp(){if(this.getClass().equals(testClass)){return;}// do the setup - once per concrete test class.....
testClass =this.getClass();}}
Esto debería funcionar para un solo setUp()método no estático , pero no puedo producir un equivalente tearDown()sin perderme en un mundo de reflexión compleja ... ¡Puntos de recompensa para cualquiera que pueda!
Editar:
descubrí mientras depuraba que la clase también se instancia antes de cada prueba. Supongo que la anotación @BeforeClass es la mejor aquí.
También puede configurar el constructor, la clase de prueba es una clase después de todo. No estoy seguro de si es una mala práctica porque casi todos los demás métodos están anotados, pero funciona. Podrías crear un constructor como ese:
public UT (){// initialize once here}@Test// Some test here...
El ctor se llamará antes de las pruebas porque no son estáticas.
con @BeforeAllMethods/ @AfterAllMethodsannotation puede ejecutar cualquier método en la clase Test en un contexto de instancia, donde todos los valores inyectados están disponibles.
Agregue a su clase abstracta base (me refiero a la clase abstracta donde inicializa su controlador en el método setUpDriver () ) esta parte del código:
privatestaticboolean started =false;static{if(!started){
started =true;try{
setUpDriver();//method where you initialize your driver}catch(MalformedURLException e){}}}
Y ahora, si sus clases de prueba se extienden desde la clase abstracta Base -> el método setUpDriver () se ejecutará antes del primer @Test solo UNA vez por ejecución.
Use el método @PostConstruct de Spring para hacer todo el trabajo de inicialización y este método se ejecuta antes de que se ejecute cualquiera de las @Test
Respuestas:
Aunque estoy de acuerdo con @assylias en que usar
@BeforeClass
es una solución clásica, no siempre es conveniente. El método anotado con@BeforeClass
debe ser estático. Es muy inconveniente para algunas pruebas que necesitan una instancia de caso de prueba. Por ejemplo, pruebas basadas en Spring que usan@Autowired
funcionar con servicios definidos en el contexto de Spring.En este caso, personalmente uso el
setUp()
método regular anotado con@Before
anotación y administro mi bandera personalizadastatic
(!)boolean
:fuente
setUp()
método esté en una superclase: he publicado una respuesta a continuación para intentar resolver esto.Puede utilizar la
BeforeClass
anotación :fuente
TheClassYouWant.class
lugar de su llamada getClass ()? Esto es real de Java:String.class.getName()
.JUnit 5 ahora tiene una anotación @BeforeAll:
¡Las anotaciones del ciclo de vida de JUnit 5 parecen haberlo hecho bien finalmente! Puede adivinar qué anotaciones están disponibles sin siquiera mirar (por ejemplo, @BeforeEach @AfterAll)
fuente
@BeforeClass
, tiene que serlostatic
. La solución de IMO @ AlexR es mejor.Cuando
setUp()
está en una superclase de la clase de prueba (por ejemplo, aAbstractTestBase
continuación), la respuesta aceptada se puede modificar de la siguiente manera:Esto debería funcionar para un solo
setUp()
método no estático , pero no puedo producir un equivalentetearDown()
sin perderme en un mundo de reflexión compleja ... ¡Puntos de recompensa para cualquiera que pueda!fuente
Editar: descubrí mientras depuraba que la clase también se instancia antes de cada prueba. Supongo que la anotación @BeforeClass es la mejor aquí.
También puede configurar el constructor, la clase de prueba es una clase después de todo. No estoy seguro de si es una mala práctica porque casi todos los demás métodos están anotados, pero funciona. Podrías crear un constructor como ese:
El ctor se llamará antes de las pruebas porque no son estáticas.
fuente
Pruebe esta solución: https://stackoverflow.com/a/46274919/907576 :
con
@BeforeAllMethods
/@AfterAllMethods
annotation puede ejecutar cualquier método en la clase Test en un contexto de instancia, donde todos los valores inyectados están disponibles.fuente
Mi solución sucia es:
Lo uso como base para todos mis testCases.
fuente
Si no desea forzar la declaración de una variable que se establece y se verifica en cada subprueba, agregar esto a una SuperTest podría hacer:
fuente
Resolví este problema así:
Agregue a su clase abstracta base (me refiero a la clase abstracta donde inicializa su controlador en el método setUpDriver () ) esta parte del código:
Y ahora, si sus clases de prueba se extienden desde la clase abstracta Base -> el método setUpDriver () se ejecutará antes del primer @Test solo UNA vez por ejecución.
fuente
Use el método @PostConstruct de Spring para hacer todo el trabajo de inicialización y este método se ejecuta antes de que se ejecute cualquiera de las @Test
fuente