Junit4: Anular la regla ExpectedException

Creado en 5 may. 2019  ·  22Comentarios  ·  Fuente: junit-team/junit4

Después de actualizar Spring Framework a JUnit 4.13 beta 3, noté que org.junit.rules.ExpectedException ahora es @Deprecated , y este cambio genera muchas advertencias en una base de código grande como esa.

Dado que 4.13 es la última versión prevista en la línea 4.x, no creo que tenga sentido desaprobar una característica tan comúnmente utilizada y compatible.

Si mantiene la depreciación en su lugar, me temo que solo molestará a miles (¿millones?) De desarrolladores sin una buena razón.

rules

Comentario más útil

@daicoden

Estoy bastante seguro de que assertThrows devuelve lo arrojable, por lo que podría capturarlo y hacer afirmaciones como lo haría con cualquier otro objeto:

StatusRuntimeException thrown = assertThrows(StatusRuntimeException.class, () -> {
   GrpcDispatch.makeRequest(service::enableJob, JobReference.newBuilder().setName(UNKNOWN_JOB).build());
});

assertEquals(Status.INVALID_ARGUMENT, thrown.getStatus());

Todos 22 comentarios

Creo que la advertencia de desaprobación es una buena manera de indicar que hay algo mejor disponible, por lo que las advertencias no están ahí por ninguna buena razón. ExpectedException se puede usar incorrectamente fácilmente, pero todavía se recomienda en muchos lugares.

La pregunta es qué tan problemático es para nuestros usuarios tener estas advertencias de obsolescencia. En mi empresa, las advertencias de desactivación no aparecen en el momento de la compilación (solo cuando se visualiza el código).

Creo que Sam tiene razón aquí. He visto muchas compilaciones que prohíben todas las (nuevas) advertencias del compilador. Tales proyectos tendrían que pasar por todas sus clases de prueba que usan ExpectedException y agregar una anotación SuppressWarnings .

@stefanbirkner WDYT?

Hacer que los clientes supriman las advertencias parece razonable. También podrían suprimir todas las advertencias de desaprobación; Tratar las advertencias de desaprobación como errores hace que sea imposible desaprobar nada.

La obsolescencia es nuestra mejor manera de indicarle a la gente que la API anterior es problemática y que hay API mucho mejores disponibles.

Entiendo que los desarrolladores pueden suprimir las advertencias localmente dentro de un IDE; sin embargo, no estoy de acuerdo con que desaprobar una característica principal en la versión final (prevista) de JUnit 4.x sea una buena idea.

En mi humilde opinión, no es una buena idea desaprobar una característica central en la versión final de cualquier marco, especialmente uno de uso tan extendido como JUnit 4.

Como ejemplo concreto, en el conjunto de pruebas principal de Spring Framework ahora tenemos miles de advertencias de obsolescencia para el uso de la regla ExpectedException .

Las únicas opciones que tenemos son:

  1. Suprima las advertencias de obsolescencia en cada clase de prueba que usa ExpectedException .
  2. Migre lejos del uso de ExpectedException en cada clase de prueba que lo use.
  3. Hacer nada.

Si vamos con el n. ° 1, es probable que podamos lograrlo con algunas secuencias de comandos que supriman las advertencias a nivel de clase, pero eso suprimiría las advertencias de obsolescencia de otras API que probablemente queramos / necesitemos conocer y no ignorar. De lo contrario, tenemos que pasar manualmente por cada clase de prueba y desaprobar cada uso de la API ExpectedException , y eso incluye cada interacción con la instancia de la regla en todos los métodos de prueba afectados.

Si seguimos con el n. ° 2, esa es una empresa importante que podría llevar varios días para llevarla a cabo manualmente. La automatización parcial puede ser posible, pero la inversión en la automatización (secuencias de comandos, etc.) puede ser bastante extensa por sí sola.

Si vamos con el n. ° 3, tendremos advertencias de depreciación innecesarias (desde nuestra perspectiva) que nublan nuestra visión de las advertencias de depreciación que realmente nos interesan, y eso conduce al efecto de ventana rota , que queremos evitar.

Hacer el n. ° 2 simplificaría la migración futura a JUnit 5.

Hacer el n. ° 2 simplificaría la migración futura a JUnit 5.

Seguro. Eso facilitaría potencialmente la migración a JUnit Jupiter o AssertJ; sin embargo, no todo el mundo quiere o necesita migrar los conjuntos de pruebas existentes.

Por tanto, no lo considero un argumento válido.

No estoy seguro de por qué esta probablemente sea la última versión de JUnit 4.x esté relacionada con si desaprobamos las cosas o no. JUnit rara vez elimina _cualquier_ API, por lo que la desaprobación no ha sido una indicación de que una API va a desaparecer. Es una indicación de que es posible que la API no sea compatible o que existan mejores API. Ambos son ciertos en este caso (hemos rechazado los cambios propuestos a esta regla porque creemos que es mejor que la gente use assertThrows() )

Sugiero que Spring migre lentamente desde ExpectedException . Unos pocos días de trabajo repartidos en unos meses para llegar a pruebas más seguras y fáciles de entender parece una victoria. El beneficio es que otras personas menos familiarizadas con los cambios recientes en JUnit sabrán que deben evitar ExpectedException

ErrorProne tiene una verificación para esto que proporcionará una solución recomendada, por lo que no creo que se necesiten varios días para resolver este problema.

TL; DR Estoy a favor de desaprobar ExpectedException.

@kcooney ya dijo que la desaprobación en JUnit 4 no significa que eliminaremos cosas porque una decisión de diseño de JUnit 4 es evitar cambios rotos. Por lo tanto, no es tan importante si esta es la última versión 4.x o no.

Creo que es útil tener la desaprobación porque le dice a la gente que hay una mejor manera de verificar las excepciones. Trabajé para algunas empresas durante los últimos 10 años y siempre hay gente que se sorprende de que haya algo mejor que @Test(expected=ABeautifulException.class . La obsolescencia es valiosa para ellos porque por lo general echan un vistazo cuando ven código tachado en el IDE.

Las advertencias de obsolescencia pueden resultar molestas porque no es necesario corregirlas. El origen de esto es la forma en que JUnit 4 usa la desaprobación. Por lo tanto, creo que no hay solución para esto aparte de no desaprobar ExpectedException (y finalmente no usar la desaprobación en absoluto).

Por último, pero no menos importante, hay empresas cuyas compilaciones se rompen en caso de advertencias de obsolescencia y, por lo tanto, no pueden actualizarse fácilmente a JUnit 4.13. No estoy seguro de cuántas de estas empresas existen, que son empresas que a) tienen esta política de compilación yb) quieren actualizar a JUnit 4.13.

@sdeleuze , @ mp911de y @rweisleder votaron a favor de revertir la desaprobación. Sería útil que nos dijera por qué desea que no se desaproveche.

Sólo por un poco más de información, que tomó cerca de 1,5 días para migrar código base de primavera marco de JUnit de ExpectedException a de AssertJ assertThatExceptionOfType .

Una pequeña sugerencia que podría ayudar es considerar desaprobar solo el método ExpectedException.none() lugar de toda la clase. Esto todavía le daría a la gente la advertencia, pero haría mucho más fácil suprimirlo de forma aislada.

ExpectedException JUnit se usa como un tipo primitivo en pruebas que realizan aserciones de excepción más extensas que solo @Test(expected = …) . Por lo general, estas son pruebas que se basan en Hamcrest o afirmaciones integradas y no tanto en AssertJ o similares.

Tener JUnit 4.13 es una señal de mantenimiento activo. Es probable que las bases de código que permanecen en la API JUnit 4.x no se migren pronto a la API JUnit 5, sino que mantienen la API con la posibilidad de migrar al motor Vintage. Tener una clase repentinamente obsoleta generó nuevas advertencias y un esfuerzo adicional para migrar a una utilidad de aserción diferente, aunque el código aún podría estar funcionando.

He observado obsolescencias y la introducción de API mejoradas varias veces. En la mayoría de los casos, la obsolescencia ayudó a los mantenedores y no a los usuarios reales. En el lado del usuario, esto generó principalmente un esfuerzo sin ningún beneficio.

Una pequeña sugerencia que podría ayudar es considerar desaprobar solo el método ExpectedException.none() lugar de toda la clase. Esto todavía le daría a la gente la advertencia, pero haría mucho más fácil suprimirlo de forma aislada.

Si el equipo de JUnit 4 decide no desestimar la regla ExpectedException , creo que este sería un buen compromiso.

Me pregunto si @Test(expected = ...) también debería quedar obsoleto ya que ahora tenemos assertThrows . O al menos la referencia a ExpectedException debe eliminarse del Javadoc del atributo.

Me pregunto si @Test(expected = ...) también debería quedar obsoleto ya que ahora tenemos assertThrows . O al menos la referencia a ExpectedException debe eliminarse del Javadoc del atributo.

Sí, es probable que el atributo expected en org.junit.Test esté obsoleto. En cualquier caso, el Javadoc de nivel de clase para org.junit.Test y el atributo expected deben actualizarse para recomendar el uso de assertThrows .

Yo estaría a favor de desaprobar solo ExpectedException.none() .

Creo que usar la desaprobación para anunciar una nueva característica es un enfoque de mano dura que dañaría la confianza en el proceso de desarrollo de JUnit y retrasaría la adopción de la versión 4.13.

Debería haber una superposición de versiones cuando coexistan la función antigua y la nueva sin que ninguna de las dos quede obsoleta. Como han señalado otros, tener dependencias de métodos obsoletos es un inconveniente para muchos proyectos que mantienen alta la calidad del código. Eso significa que la actualización de JUnit debe estar en la misma confirmación que los cambios para migrar todo el código base.

Creo que la única situación en la que una función está obsoleta y el reemplazo se agrega en la misma versión es cuando la función está fundamentalmente rota y debe migrarse inmediatamente. No creo que ExpectedException esté fundamentalmente roto.

A menudo verifico dependencias más nuevas para ver qué cambios se avecinan y cómo puedo preparar mi código. Entonces, ¿cómo preparo mi código para la migración de JUnit 4.13? Sé que ExpectedException quedará obsoleto, pero todavía no tengo un buen reemplazo. Necesitaría implementar mi propio código para detectar y verificar excepciones para evitar ese lío por completo.

El hecho de que 4.13 sea la versión final 4.x esté planificada, no es una buena razón para abandonar las prácticas sensatas de desarrollo de software. De hecho, 4.13 se puede lanzar sin la depreciación ExpectedException y 4.14 se puede lanzar al día siguiente con la depreciación. Eso transmitiría el mensaje y daría a los desarrolladores un pequeño respiro para realizar la migración sin la presión indebida.

Debería haber una superposición de versiones cuando coexistan la función antigua y la nueva sin que ninguna de las dos quede obsoleta.

¿Por qué? La intención de la desaprobación es hacer que las personas conozcan la nueva API, en nuestra opinión, una mejor.

También estoy de acuerdo en que solo deberíamos desaprobar ExpectedException.none() para hacerlo menos invasivo.

@stefanbirkner ¿Estás de acuerdo?

@marcphilipp Cuando actualizo mi código, quiero que las cosas funcionen sin depreciaciones a menos que esté haciendo algo mal. Es demasiado intrusivo desaprobar una función y proporcionar un reemplazo al mismo tiempo. En cambio, las notas de la versión 4.13 deberían decir que ExpectedException quedará obsoleto en la próxima versión y sugerirán un reemplazo. De esa manera, tendré una forma de migrar mi código paso a paso sin tener que permitir obsolescencias mientras tanto. Eso es solo una cortesía básica para los usuarios que han estado usando JUnit 4.12 durante años y no quieren grandes cambios.

En mi humilde opinión, el punto de una advertencia de obsolescencia _es_ darle tiempo para migrar su código hacia el reemplazo. Si necesita evitar la advertencia de obsolescencia, puede crear su propio método de fábrica temporal que delegue a ExpectedException.none() .

De acuerdo, las relaciones públicas mitigan el problema hasta cierto punto, definitivamente es un paso en la dirección correcta. ¡Gracias por hacer eso!

En lugar de sugerir que los usuarios implementen un método de fábrica, el código podría proporcionar su propio método no obsoleto (tal vez none con un argumento adicional).

Todavía no entiendo la resistencia a darles a los usuarios una versión transitoria. Al implementar un cambio incompatible, es muy común hacer una versión que permita la transición sin forzarla de ninguna manera.

Sin una versión de transición, los usuarios probarían 4.13, verían toneladas de advertencias y volverían a 4.12.

Con una versión de transición, los usuarios probarían 4.14, verían toneladas de advertencias, probarían 4.13, no verían advertencias, usarían. Algunos actualizarían a 4.14 rápidamente, otros se quedarían felizmente con 4.13 a menos que tengan una buena razón para actualizar. Debería estar bien de cualquier manera.

Tampoco entiendo por qué debería haber ocurrido esta desaprobación en JUnit 4. Estoy migrando a JUnit 5 usando junit-vintage-engine para admitir ambas versiones temporalmente. Pero esto saca a JUnit 4.13 que, por alguna razón, decidió que era una buena idea desaprobar las cosas (que han sido compatibles durante años) en lo que probablemente sea uno de sus últimos lanzamientos.

Tampoco parece haber ninguna razón para la desaprobación más que señalar una alternativa; esto podría haberse logrado al menos ofreciendo una alternativa (hacer público el constructor u ofrecer otro método estático: mensaje comunicado).

El código ExpectedException tal como está ahora continuará funcionando (y seguirá funcionando incluso en versiones posteriores), ya que simplemente usa las características básicas de JUnit que se ofrecen de una manera conveniente (en ese momento).

¿Cuál es el reemplazo para afirmar propiedades sobre la excepción ...

código antiguo:

thrown.expect(StatusRuntimeException.class);
thrown.expect(its(StatusRuntimeException::getStatus, toBe(Status.INVALID_ARGUMENT)));
GrpcDispatch.makeRequest(service::enableJob, JobReference.newBuilder().setName(UNKNOWN_JOB).build());

Parece que a assertThrows le falta una versión de emparejamiento para permitir que la excepción esperada quede totalmente obsoleta ... Sé que es demasiado tarde, pero ¿nadie más tiene este problema?

@daicoden

Estoy bastante seguro de que assertThrows devuelve lo arrojable, por lo que podría capturarlo y hacer afirmaciones como lo haría con cualquier otro objeto:

StatusRuntimeException thrown = assertThrows(StatusRuntimeException.class, () -> {
   GrpcDispatch.makeRequest(service::enableJob, JobReference.newBuilder().setName(UNKNOWN_JOB).build());
});

assertEquals(Status.INVALID_ARGUMENT, thrown.getStatus());
¿Fue útil esta página
0 / 5 - 0 calificaciones