Mejora del proceso de programación personal mediante Sensei
Para este post, he recreado un enfoque de codificación "malo" que utilicé cuando estaba aprendiendo JUnit, y demostraré cómo convertir el patrón "malo" en un patrón de codificación acordado, y "mejor", utilizando Sensei.
Cuando estaba aprendiendo JUnit, sólo podía retener una cantidad de cosas en mi cabeza en un momento dado. Constantemente olvidaba cómo omitir las pruebas cuando no funcionaban.
Si trabajamos en equipo, podemos utilizar las revisiones de código en los pull requests para ayudar a reforzar los estilos de codificación. Y podemos acortar el ciclo de retroalimentación cuando programamos en pareja con un programador más experimentado.
También podemos aumentar nuestro proceso con herramientas y hacer que éstas nos indiquen que hagamos lo correcto. Thoughtworks lo describió como "herramientas en lugar de reglas", en su lista de Technology Radar para Sensei, para: "facilitar que se haga lo correcto en lugar de aplicar normas y procedimientos de gobernanza tipo lista de control"
Desactivación de una prueba JUnit
Lo ideal sería, como todos sabemos, utilizar la anotación @Disabled y escribir:
@Disabled
@Test
void canWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
Pero, al aprender, tuve que entrenarme para usar @Disabled.
Cuando me olvidaba de cómo desactivar un método de prueba, eliminaba la anotación @Test y cambiaba el nombre de la prueba:
class SkipThisTest {
void SKIPTHIScanWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
}
No era bueno, pero hacía el trabajo. No tenía algo como Sensei para ayudarme a recordar y entonces caí en el uso de patrones de codificación pobres.
Las tareas que he asumido para este puesto son:
- Cree una regla que encuentre los métodos que han sido "omitidos" o "deshabilitados" cambiando el nombre del método.
- Crea un QuickFix para renombrar el método y añadir una anotación @Test y @Disabled.
Ajustes de la receta
El primer paso que doy con Sensei es "añadir nueva receta" y buscar el patrón de codificación sobre el que quiero que actúe la receta.
Nombre: JUnit: Hacer @Disabled @Test de SKIPTHIS
Descripción breve: Deje de nombrar los métodos SKIPTHIS, utilice en su lugar @Disabled @Test
Y mi búsqueda es muy sencilla. Utilizo una regex básica para que coincida con el nombre del método.
búsqueda:
método:
nombre:
coincide: "SKIPTHIS.*"
Ajustes de QuickFix
El QuickFix es un poco más complicado porque va a reescribir el código, y voy a utilizar algunos pasos para lograr mi código final.
Quiero hacerlo:
- añadir una anotación @Test al método
- añadir una anotación @Disabled al método
- modificar el nombre del método
Añadir las anotaciones es bastante simple usando el arreglo addAnnotation. Si utilizo un nombre completo para la anotación, Sensei añadirá automáticamente las importaciones por mí.
availableFixes:
- name: "Add @Disabled and @Test Annotation"
acciones:
- addAnnotation:
annotation: "@org.junit.jupiter.api.Test"
- addAnnotation:
annotation: "@org.junit.jupiter.api.Disabled"
El renombramiento real parece un poco más complicado, pero sólo estoy usando un reemplazo regex, y la forma genérica de hacer esto con Sensei es usar sed en una acción de reescritura.
Como las acciones de reescritura son plantillas de Mustache, Sensei tiene algunas extensiones funcionales en el mecanismo de plantillas. Una función se representa con {{#...}} así que para sed la función es {{#sed}}. La función toma dos argumentos separados por comas.
El primer argumento es la declaración sed:
- s/(.*) SKIPTHIS(.*)/$1 $2/
El segundo argumento es el String al que aplicar la sentencia sed, que en este caso es el propio método, y que se representa en las variables Mustache como:
- {{{.}}}
Dándome la acción de reescribir:
- Reescritura:
to: "{{#sed}}s/(.*) SKIPTHIS(.*)/$1 $2/,{{{.}}}{{/sed}}"
La implementación de sed requiere que cuando los argumentos mismos contengan comas, se envuelvan con {{#encodeString}} y {{/encodeString}} - Por ejemplo, {{#encodeString}}{{{.}}}{{/encodeString}}
Receta inversa
Dado que se trata de un ejemplo, y es posible que queramos utilizarlo en demostraciones, quería explorar cómo revertir el cambio anterior utilizando una receta de Sensei .
Pensando bien quiero encontrar un método anotado con @Disabled pero sólo en la clase SkipThisTest donde hago la demostración:
Nombre: JUnit: demo en SkipThisTest eliminar @Disabled y volver a SKIPTHIS
Descripción breve: eliminar @Disabled y volver a SKIPTHIS para fines de demostración en el proyecto
Nivel: advertencia
La búsqueda de configuraciones de recetas es muy simple, ya que coincide con la anotación en una clase específica.
búsqueda:
método:
anotación:
tipo: "Disabled"
in:
class:
name: "SkipThisTest"
Para evitar que el código se vea como un error, definí la configuración general de la receta como una Advertencia. Las advertencias se muestran con resaltados en el código y no hace que el código parezca que tiene un problema mayor.
Para el Quick fix, ya que hemos emparejado el método, utilizo la acción de reescritura y relleno la plantilla utilizando las variables.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
Agrego todas las variables excepto el modificador (ya que quiero deshacerme de las anotaciones) y añado el texto SKIPTHIS en la plantilla.
Este arreglo tiene la debilidad de que al eliminar los modificadores, elimino también cualquier otra anotación.
Añadir otra acción
Puedo añadir otro arreglo con nombre, para que me dé una opción cuando se use el alt+enter para mostrar el QuickFix.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
- name: "Remove Disabled, keep other annotations, and rename to SKIPTHIS..."
actions:
- rewrite:
to: "{{#sed}}s/(@Disabled\n.*@Test)//,{{{ modifierList }}}{{/sed}}\n\
{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
Aquí, he añadido una línea adicional en el nuevo Quick Fix.
{{sed}}s/(@Disabled\n.*@Test)//,{{modifierList}} {{sed}}
Esto toma la lista de modificadores, la codifica como una cadena, luego utiliza sed para eliminar la línea con @Disabled de la cadena, pero deja todas las demás líneas del modificador, es decir, deja todas las demás anotaciones.
NOTA: Recuerde añadir el "," en el sed, de lo contrario verá un comentario añadido a su vista previa. Así es como Sensei le avisa de los errores de sintaxis en el comando sed.
/* e.g: {{#sed}}s/all/world/,helloall{{/sed}} */
Llamadas sed anidadas
Tuve la suerte de poder coincidir con @Disabled y @Test en una sola búsqueda y reemplazo.
Si el código es más complicado y quiero tener una secuencia de comandos sed entonces puedo hacerlo anidando:
{{#sed}}s/@Test//,{{#sed}}s/@Disabled\n//,{{{ modifierList }}}{{/sed}}{{/sed}}
En el ejemplo anterior, aplico el reemplazo @Test a los resultados de aplicar el reemplazo @Disabled en el {{{ modifierList }}}.
Resumen
sed es una forma muy flexible de lograr la reescritura de código y es posible anidar las llamadas a la función sed para obtener condiciones de reescritura complicadas.
Este tipo de recetas suelen ser temporales porque las utilizamos para mejorar nuestro proceso de programación, y una vez que hayamos creado la memoria muscular y ya no utilicemos el mal patrón de programación podemos eliminarlo o desactivarlo en el Libro de Cocina.
---
Puedes instalar Sensei desde IntelliJ usando "Preferences \ Plugins" (Mac) o "Settings \ Plugins" (Windows) y luego sólo busca "sensei secure code".
Todo el código de esta entrada del blog se puede encontrar en GitHub en el módulo `junitexamples` de nuestro repositorio de ejemplos del blog https://github.com/SecureCodeWarrior/sensei-blog-examples
Aprenda a utilizar las revisiones de código en las solicitudes de extracción para ayudar a reforzar los estilos de codificación. Y acortar el ciclo de retroalimentación al programar en pareja con un programador más experimentado.
Alan Richardson cuenta con más de veinte años de experiencia profesional en TI, trabajando como desarrollador y en todos los niveles de la jerarquía de pruebas, desde probador hasta jefe de pruebas. Jefe de Relaciones con los Desarrolladores en Secure Code Warrior, trabaja directamente con los equipos, para mejorar el desarrollo de un código seguro de calidad. Alan es autor de cuatro libros, entre ellos "Dear Evil Tester" y "Java For Testers". Alan también ha creado una formación en línea courses para ayudar a la gente a aprender las pruebas técnicas de la web y Selenium WebDriver con Java. Alan publica sus escritos y vídeos de formación en SeleniumSimplified.com, EvilTester.com, JavaForTesters.com, y CompendiumDev.co.uk.
Secure Code Warrior está a disposición de su organización para ayudarle a proteger el código a lo largo de todo el ciclo de vida de desarrollo de software y crear una cultura en la que la ciberseguridad sea una prioridad. Tanto si es director de AppSec, desarrollador, CISO o cualquier persona implicada en la seguridad, podemos ayudar a su organización a reducir los riesgos asociados a un código inseguro.
Reservar una demostraciónAlan Richardson cuenta con más de veinte años de experiencia profesional en TI, trabajando como desarrollador y en todos los niveles de la jerarquía de pruebas, desde probador hasta jefe de pruebas. Jefe de Relaciones con los Desarrolladores en Secure Code Warrior, trabaja directamente con los equipos, para mejorar el desarrollo de un código seguro de calidad. Alan es autor de cuatro libros, entre ellos "Dear Evil Tester" y "Java For Testers". Alan también ha creado una formación en línea courses para ayudar a la gente a aprender las pruebas técnicas de la web y Selenium WebDriver con Java. Alan publica sus escritos y vídeos de formación en SeleniumSimplified.com, EvilTester.com, JavaForTesters.com, y CompendiumDev.co.uk.
Para este post, he recreado un enfoque de codificación "malo" que utilicé cuando estaba aprendiendo JUnit, y demostraré cómo convertir el patrón "malo" en un patrón de codificación acordado, y "mejor", utilizando Sensei.
Cuando estaba aprendiendo JUnit, sólo podía retener una cantidad de cosas en mi cabeza en un momento dado. Constantemente olvidaba cómo omitir las pruebas cuando no funcionaban.
Si trabajamos en equipo, podemos utilizar las revisiones de código en los pull requests para ayudar a reforzar los estilos de codificación. Y podemos acortar el ciclo de retroalimentación cuando programamos en pareja con un programador más experimentado.
También podemos aumentar nuestro proceso con herramientas y hacer que éstas nos indiquen que hagamos lo correcto. Thoughtworks lo describió como "herramientas en lugar de reglas", en su lista de Technology Radar para Sensei, para: "facilitar que se haga lo correcto en lugar de aplicar normas y procedimientos de gobernanza tipo lista de control"
Desactivación de una prueba JUnit
Lo ideal sería, como todos sabemos, utilizar la anotación @Disabled y escribir:
@Disabled
@Test
void canWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
Pero, al aprender, tuve que entrenarme para usar @Disabled.
Cuando me olvidaba de cómo desactivar un método de prueba, eliminaba la anotación @Test y cambiaba el nombre de la prueba:
class SkipThisTest {
void SKIPTHIScanWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
}
No era bueno, pero hacía el trabajo. No tenía algo como Sensei para ayudarme a recordar y entonces caí en el uso de patrones de codificación pobres.
Las tareas que he asumido para este puesto son:
- Cree una regla que encuentre los métodos que han sido "omitidos" o "deshabilitados" cambiando el nombre del método.
- Crea un QuickFix para renombrar el método y añadir una anotación @Test y @Disabled.
Ajustes de la receta
El primer paso que doy con Sensei es "añadir nueva receta" y buscar el patrón de codificación sobre el que quiero que actúe la receta.
Nombre: JUnit: Hacer @Disabled @Test de SKIPTHIS
Descripción breve: Deje de nombrar los métodos SKIPTHIS, utilice en su lugar @Disabled @Test
Y mi búsqueda es muy sencilla. Utilizo una regex básica para que coincida con el nombre del método.
búsqueda:
método:
nombre:
coincide: "SKIPTHIS.*"
Ajustes de QuickFix
El QuickFix es un poco más complicado porque va a reescribir el código, y voy a utilizar algunos pasos para lograr mi código final.
Quiero hacerlo:
- añadir una anotación @Test al método
- añadir una anotación @Disabled al método
- modificar el nombre del método
Añadir las anotaciones es bastante simple usando el arreglo addAnnotation. Si utilizo un nombre completo para la anotación, Sensei añadirá automáticamente las importaciones por mí.
availableFixes:
- name: "Add @Disabled and @Test Annotation"
acciones:
- addAnnotation:
annotation: "@org.junit.jupiter.api.Test"
- addAnnotation:
annotation: "@org.junit.jupiter.api.Disabled"
El renombramiento real parece un poco más complicado, pero sólo estoy usando un reemplazo regex, y la forma genérica de hacer esto con Sensei es usar sed en una acción de reescritura.
Como las acciones de reescritura son plantillas de Mustache, Sensei tiene algunas extensiones funcionales en el mecanismo de plantillas. Una función se representa con {{#...}} así que para sed la función es {{#sed}}. La función toma dos argumentos separados por comas.
El primer argumento es la declaración sed:
- s/(.*) SKIPTHIS(.*)/$1 $2/
El segundo argumento es el String al que aplicar la sentencia sed, que en este caso es el propio método, y que se representa en las variables Mustache como:
- {{{.}}}
Dándome la acción de reescribir:
- Reescritura:
to: "{{#sed}}s/(.*) SKIPTHIS(.*)/$1 $2/,{{{.}}}{{/sed}}"
La implementación de sed requiere que cuando los argumentos mismos contengan comas, se envuelvan con {{#encodeString}} y {{/encodeString}} - Por ejemplo, {{#encodeString}}{{{.}}}{{/encodeString}}
Receta inversa
Dado que se trata de un ejemplo, y es posible que queramos utilizarlo en demostraciones, quería explorar cómo revertir el cambio anterior utilizando una receta de Sensei .
Pensando bien quiero encontrar un método anotado con @Disabled pero sólo en la clase SkipThisTest donde hago la demostración:
Nombre: JUnit: demo en SkipThisTest eliminar @Disabled y volver a SKIPTHIS
Descripción breve: eliminar @Disabled y volver a SKIPTHIS para fines de demostración en el proyecto
Nivel: advertencia
La búsqueda de configuraciones de recetas es muy simple, ya que coincide con la anotación en una clase específica.
búsqueda:
método:
anotación:
tipo: "Disabled"
in:
class:
name: "SkipThisTest"
Para evitar que el código se vea como un error, definí la configuración general de la receta como una Advertencia. Las advertencias se muestran con resaltados en el código y no hace que el código parezca que tiene un problema mayor.
Para el Quick fix, ya que hemos emparejado el método, utilizo la acción de reescritura y relleno la plantilla utilizando las variables.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
Agrego todas las variables excepto el modificador (ya que quiero deshacerme de las anotaciones) y añado el texto SKIPTHIS en la plantilla.
Este arreglo tiene la debilidad de que al eliminar los modificadores, elimino también cualquier otra anotación.
Añadir otra acción
Puedo añadir otro arreglo con nombre, para que me dé una opción cuando se use el alt+enter para mostrar el QuickFix.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
- name: "Remove Disabled, keep other annotations, and rename to SKIPTHIS..."
actions:
- rewrite:
to: "{{#sed}}s/(@Disabled\n.*@Test)//,{{{ modifierList }}}{{/sed}}\n\
{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
Aquí, he añadido una línea adicional en el nuevo Quick Fix.
{{sed}}s/(@Disabled\n.*@Test)//,{{modifierList}} {{sed}}
Esto toma la lista de modificadores, la codifica como una cadena, luego utiliza sed para eliminar la línea con @Disabled de la cadena, pero deja todas las demás líneas del modificador, es decir, deja todas las demás anotaciones.
NOTA: Recuerde añadir el "," en el sed, de lo contrario verá un comentario añadido a su vista previa. Así es como Sensei le avisa de los errores de sintaxis en el comando sed.
/* e.g: {{#sed}}s/all/world/,helloall{{/sed}} */
Llamadas sed anidadas
Tuve la suerte de poder coincidir con @Disabled y @Test en una sola búsqueda y reemplazo.
Si el código es más complicado y quiero tener una secuencia de comandos sed entonces puedo hacerlo anidando:
{{#sed}}s/@Test//,{{#sed}}s/@Disabled\n//,{{{ modifierList }}}{{/sed}}{{/sed}}
En el ejemplo anterior, aplico el reemplazo @Test a los resultados de aplicar el reemplazo @Disabled en el {{{ modifierList }}}.
Resumen
sed es una forma muy flexible de lograr la reescritura de código y es posible anidar las llamadas a la función sed para obtener condiciones de reescritura complicadas.
Este tipo de recetas suelen ser temporales porque las utilizamos para mejorar nuestro proceso de programación, y una vez que hayamos creado la memoria muscular y ya no utilicemos el mal patrón de programación podemos eliminarlo o desactivarlo en el Libro de Cocina.
---
Puedes instalar Sensei desde IntelliJ usando "Preferences \ Plugins" (Mac) o "Settings \ Plugins" (Windows) y luego sólo busca "sensei secure code".
Todo el código de esta entrada del blog se puede encontrar en GitHub en el módulo `junitexamples` de nuestro repositorio de ejemplos del blog https://github.com/SecureCodeWarrior/sensei-blog-examples
Para este post, he recreado un enfoque de codificación "malo" que utilicé cuando estaba aprendiendo JUnit, y demostraré cómo convertir el patrón "malo" en un patrón de codificación acordado, y "mejor", utilizando Sensei.
Cuando estaba aprendiendo JUnit, sólo podía retener una cantidad de cosas en mi cabeza en un momento dado. Constantemente olvidaba cómo omitir las pruebas cuando no funcionaban.
Si trabajamos en equipo, podemos utilizar las revisiones de código en los pull requests para ayudar a reforzar los estilos de codificación. Y podemos acortar el ciclo de retroalimentación cuando programamos en pareja con un programador más experimentado.
También podemos aumentar nuestro proceso con herramientas y hacer que éstas nos indiquen que hagamos lo correcto. Thoughtworks lo describió como "herramientas en lugar de reglas", en su lista de Technology Radar para Sensei, para: "facilitar que se haga lo correcto en lugar de aplicar normas y procedimientos de gobernanza tipo lista de control"
Desactivación de una prueba JUnit
Lo ideal sería, como todos sabemos, utilizar la anotación @Disabled y escribir:
@Disabled
@Test
void canWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
Pero, al aprender, tuve que entrenarme para usar @Disabled.
Cuando me olvidaba de cómo desactivar un método de prueba, eliminaba la anotación @Test y cambiaba el nombre de la prueba:
class SkipThisTest {
void SKIPTHIScanWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
}
No era bueno, pero hacía el trabajo. No tenía algo como Sensei para ayudarme a recordar y entonces caí en el uso de patrones de codificación pobres.
Las tareas que he asumido para este puesto son:
- Cree una regla que encuentre los métodos que han sido "omitidos" o "deshabilitados" cambiando el nombre del método.
- Crea un QuickFix para renombrar el método y añadir una anotación @Test y @Disabled.
Ajustes de la receta
El primer paso que doy con Sensei es "añadir nueva receta" y buscar el patrón de codificación sobre el que quiero que actúe la receta.
Nombre: JUnit: Hacer @Disabled @Test de SKIPTHIS
Descripción breve: Deje de nombrar los métodos SKIPTHIS, utilice en su lugar @Disabled @Test
Y mi búsqueda es muy sencilla. Utilizo una regex básica para que coincida con el nombre del método.
búsqueda:
método:
nombre:
coincide: "SKIPTHIS.*"
Ajustes de QuickFix
El QuickFix es un poco más complicado porque va a reescribir el código, y voy a utilizar algunos pasos para lograr mi código final.
Quiero hacerlo:
- añadir una anotación @Test al método
- añadir una anotación @Disabled al método
- modificar el nombre del método
Añadir las anotaciones es bastante simple usando el arreglo addAnnotation. Si utilizo un nombre completo para la anotación, Sensei añadirá automáticamente las importaciones por mí.
availableFixes:
- name: "Add @Disabled and @Test Annotation"
acciones:
- addAnnotation:
annotation: "@org.junit.jupiter.api.Test"
- addAnnotation:
annotation: "@org.junit.jupiter.api.Disabled"
El renombramiento real parece un poco más complicado, pero sólo estoy usando un reemplazo regex, y la forma genérica de hacer esto con Sensei es usar sed en una acción de reescritura.
Como las acciones de reescritura son plantillas de Mustache, Sensei tiene algunas extensiones funcionales en el mecanismo de plantillas. Una función se representa con {{#...}} así que para sed la función es {{#sed}}. La función toma dos argumentos separados por comas.
El primer argumento es la declaración sed:
- s/(.*) SKIPTHIS(.*)/$1 $2/
El segundo argumento es el String al que aplicar la sentencia sed, que en este caso es el propio método, y que se representa en las variables Mustache como:
- {{{.}}}
Dándome la acción de reescribir:
- Reescritura:
to: "{{#sed}}s/(.*) SKIPTHIS(.*)/$1 $2/,{{{.}}}{{/sed}}"
La implementación de sed requiere que cuando los argumentos mismos contengan comas, se envuelvan con {{#encodeString}} y {{/encodeString}} - Por ejemplo, {{#encodeString}}{{{.}}}{{/encodeString}}
Receta inversa
Dado que se trata de un ejemplo, y es posible que queramos utilizarlo en demostraciones, quería explorar cómo revertir el cambio anterior utilizando una receta de Sensei .
Pensando bien quiero encontrar un método anotado con @Disabled pero sólo en la clase SkipThisTest donde hago la demostración:
Nombre: JUnit: demo en SkipThisTest eliminar @Disabled y volver a SKIPTHIS
Descripción breve: eliminar @Disabled y volver a SKIPTHIS para fines de demostración en el proyecto
Nivel: advertencia
La búsqueda de configuraciones de recetas es muy simple, ya que coincide con la anotación en una clase específica.
búsqueda:
método:
anotación:
tipo: "Disabled"
in:
class:
name: "SkipThisTest"
Para evitar que el código se vea como un error, definí la configuración general de la receta como una Advertencia. Las advertencias se muestran con resaltados en el código y no hace que el código parezca que tiene un problema mayor.
Para el Quick fix, ya que hemos emparejado el método, utilizo la acción de reescritura y relleno la plantilla utilizando las variables.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
Agrego todas las variables excepto el modificador (ya que quiero deshacerme de las anotaciones) y añado el texto SKIPTHIS en la plantilla.
Este arreglo tiene la debilidad de que al eliminar los modificadores, elimino también cualquier otra anotación.
Añadir otra acción
Puedo añadir otro arreglo con nombre, para que me dé una opción cuando se use el alt+enter para mostrar el QuickFix.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
- name: "Remove Disabled, keep other annotations, and rename to SKIPTHIS..."
actions:
- rewrite:
to: "{{#sed}}s/(@Disabled\n.*@Test)//,{{{ modifierList }}}{{/sed}}\n\
{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
Aquí, he añadido una línea adicional en el nuevo Quick Fix.
{{sed}}s/(@Disabled\n.*@Test)//,{{modifierList}} {{sed}}
Esto toma la lista de modificadores, la codifica como una cadena, luego utiliza sed para eliminar la línea con @Disabled de la cadena, pero deja todas las demás líneas del modificador, es decir, deja todas las demás anotaciones.
NOTA: Recuerde añadir el "," en el sed, de lo contrario verá un comentario añadido a su vista previa. Así es como Sensei le avisa de los errores de sintaxis en el comando sed.
/* e.g: {{#sed}}s/all/world/,helloall{{/sed}} */
Llamadas sed anidadas
Tuve la suerte de poder coincidir con @Disabled y @Test en una sola búsqueda y reemplazo.
Si el código es más complicado y quiero tener una secuencia de comandos sed entonces puedo hacerlo anidando:
{{#sed}}s/@Test//,{{#sed}}s/@Disabled\n//,{{{ modifierList }}}{{/sed}}{{/sed}}
En el ejemplo anterior, aplico el reemplazo @Test a los resultados de aplicar el reemplazo @Disabled en el {{{ modifierList }}}.
Resumen
sed es una forma muy flexible de lograr la reescritura de código y es posible anidar las llamadas a la función sed para obtener condiciones de reescritura complicadas.
Este tipo de recetas suelen ser temporales porque las utilizamos para mejorar nuestro proceso de programación, y una vez que hayamos creado la memoria muscular y ya no utilicemos el mal patrón de programación podemos eliminarlo o desactivarlo en el Libro de Cocina.
---
Puedes instalar Sensei desde IntelliJ usando "Preferences \ Plugins" (Mac) o "Settings \ Plugins" (Windows) y luego sólo busca "sensei secure code".
Todo el código de esta entrada del blog se puede encontrar en GitHub en el módulo `junitexamples` de nuestro repositorio de ejemplos del blog https://github.com/SecureCodeWarrior/sensei-blog-examples
Haga clic en el siguiente enlace y descargue el PDF de este recurso.
Secure Code Warrior está a disposición de su organización para ayudarle a proteger el código a lo largo de todo el ciclo de vida de desarrollo de software y crear una cultura en la que la ciberseguridad sea una prioridad. Tanto si es director de AppSec, desarrollador, CISO o cualquier persona implicada en la seguridad, podemos ayudar a su organización a reducir los riesgos asociados a un código inseguro.
Ver el informeReservar una demostraciónAlan Richardson cuenta con más de veinte años de experiencia profesional en TI, trabajando como desarrollador y en todos los niveles de la jerarquía de pruebas, desde probador hasta jefe de pruebas. Jefe de Relaciones con los Desarrolladores en Secure Code Warrior, trabaja directamente con los equipos, para mejorar el desarrollo de un código seguro de calidad. Alan es autor de cuatro libros, entre ellos "Dear Evil Tester" y "Java For Testers". Alan también ha creado una formación en línea courses para ayudar a la gente a aprender las pruebas técnicas de la web y Selenium WebDriver con Java. Alan publica sus escritos y vídeos de formación en SeleniumSimplified.com, EvilTester.com, JavaForTesters.com, y CompendiumDev.co.uk.
Para este post, he recreado un enfoque de codificación "malo" que utilicé cuando estaba aprendiendo JUnit, y demostraré cómo convertir el patrón "malo" en un patrón de codificación acordado, y "mejor", utilizando Sensei.
Cuando estaba aprendiendo JUnit, sólo podía retener una cantidad de cosas en mi cabeza en un momento dado. Constantemente olvidaba cómo omitir las pruebas cuando no funcionaban.
Si trabajamos en equipo, podemos utilizar las revisiones de código en los pull requests para ayudar a reforzar los estilos de codificación. Y podemos acortar el ciclo de retroalimentación cuando programamos en pareja con un programador más experimentado.
También podemos aumentar nuestro proceso con herramientas y hacer que éstas nos indiquen que hagamos lo correcto. Thoughtworks lo describió como "herramientas en lugar de reglas", en su lista de Technology Radar para Sensei, para: "facilitar que se haga lo correcto en lugar de aplicar normas y procedimientos de gobernanza tipo lista de control"
Desactivación de una prueba JUnit
Lo ideal sería, como todos sabemos, utilizar la anotación @Disabled y escribir:
@Disabled
@Test
void canWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
Pero, al aprender, tuve que entrenarme para usar @Disabled.
Cuando me olvidaba de cómo desactivar un método de prueba, eliminaba la anotación @Test y cambiaba el nombre de la prueba:
class SkipThisTest {
void SKIPTHIScanWeAddTwoNumbers(){
Assertions.fail("this test was skipped and should not run");
}
}
No era bueno, pero hacía el trabajo. No tenía algo como Sensei para ayudarme a recordar y entonces caí en el uso de patrones de codificación pobres.
Las tareas que he asumido para este puesto son:
- Cree una regla que encuentre los métodos que han sido "omitidos" o "deshabilitados" cambiando el nombre del método.
- Crea un QuickFix para renombrar el método y añadir una anotación @Test y @Disabled.
Ajustes de la receta
El primer paso que doy con Sensei es "añadir nueva receta" y buscar el patrón de codificación sobre el que quiero que actúe la receta.
Nombre: JUnit: Hacer @Disabled @Test de SKIPTHIS
Descripción breve: Deje de nombrar los métodos SKIPTHIS, utilice en su lugar @Disabled @Test
Y mi búsqueda es muy sencilla. Utilizo una regex básica para que coincida con el nombre del método.
búsqueda:
método:
nombre:
coincide: "SKIPTHIS.*"
Ajustes de QuickFix
El QuickFix es un poco más complicado porque va a reescribir el código, y voy a utilizar algunos pasos para lograr mi código final.
Quiero hacerlo:
- añadir una anotación @Test al método
- añadir una anotación @Disabled al método
- modificar el nombre del método
Añadir las anotaciones es bastante simple usando el arreglo addAnnotation. Si utilizo un nombre completo para la anotación, Sensei añadirá automáticamente las importaciones por mí.
availableFixes:
- name: "Add @Disabled and @Test Annotation"
acciones:
- addAnnotation:
annotation: "@org.junit.jupiter.api.Test"
- addAnnotation:
annotation: "@org.junit.jupiter.api.Disabled"
El renombramiento real parece un poco más complicado, pero sólo estoy usando un reemplazo regex, y la forma genérica de hacer esto con Sensei es usar sed en una acción de reescritura.
Como las acciones de reescritura son plantillas de Mustache, Sensei tiene algunas extensiones funcionales en el mecanismo de plantillas. Una función se representa con {{#...}} así que para sed la función es {{#sed}}. La función toma dos argumentos separados por comas.
El primer argumento es la declaración sed:
- s/(.*) SKIPTHIS(.*)/$1 $2/
El segundo argumento es el String al que aplicar la sentencia sed, que en este caso es el propio método, y que se representa en las variables Mustache como:
- {{{.}}}
Dándome la acción de reescribir:
- Reescritura:
to: "{{#sed}}s/(.*) SKIPTHIS(.*)/$1 $2/,{{{.}}}{{/sed}}"
La implementación de sed requiere que cuando los argumentos mismos contengan comas, se envuelvan con {{#encodeString}} y {{/encodeString}} - Por ejemplo, {{#encodeString}}{{{.}}}{{/encodeString}}
Receta inversa
Dado que se trata de un ejemplo, y es posible que queramos utilizarlo en demostraciones, quería explorar cómo revertir el cambio anterior utilizando una receta de Sensei .
Pensando bien quiero encontrar un método anotado con @Disabled pero sólo en la clase SkipThisTest donde hago la demostración:
Nombre: JUnit: demo en SkipThisTest eliminar @Disabled y volver a SKIPTHIS
Descripción breve: eliminar @Disabled y volver a SKIPTHIS para fines de demostración en el proyecto
Nivel: advertencia
La búsqueda de configuraciones de recetas es muy simple, ya que coincide con la anotación en una clase específica.
búsqueda:
método:
anotación:
tipo: "Disabled"
in:
class:
name: "SkipThisTest"
Para evitar que el código se vea como un error, definí la configuración general de la receta como una Advertencia. Las advertencias se muestran con resaltados en el código y no hace que el código parezca que tiene un problema mayor.
Para el Quick fix, ya que hemos emparejado el método, utilizo la acción de reescritura y relleno la plantilla utilizando las variables.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
Agrego todas las variables excepto el modificador (ya que quiero deshacerme de las anotaciones) y añado el texto SKIPTHIS en la plantilla.
Este arreglo tiene la debilidad de que al eliminar los modificadores, elimino también cualquier otra anotación.
Añadir otra acción
Puedo añadir otro arreglo con nombre, para que me dé una opción cuando se use el alt+enter para mostrar el QuickFix.
availableFixes:
- name: "Eliminar Desactivado y cambiar el nombre a SKIPTHIS..."
actions:
- rewrite:
to: "{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
- name: "Remove Disabled, keep other annotations, and rename to SKIPTHIS..."
actions:
- rewrite:
to: "{{#sed}}s/(@Disabled\n.*@Test)//,{{{ modifierList }}}{{/sed}}\n\
{{{ returnTypeElement }}} SKIPTHIS{{{ nameIdentifier }}}{{{ parameterList\
\ }}}{{{ body }}}"
target: "self"
Aquí, he añadido una línea adicional en el nuevo Quick Fix.
{{sed}}s/(@Disabled\n.*@Test)//,{{modifierList}} {{sed}}
Esto toma la lista de modificadores, la codifica como una cadena, luego utiliza sed para eliminar la línea con @Disabled de la cadena, pero deja todas las demás líneas del modificador, es decir, deja todas las demás anotaciones.
NOTA: Recuerde añadir el "," en el sed, de lo contrario verá un comentario añadido a su vista previa. Así es como Sensei le avisa de los errores de sintaxis en el comando sed.
/* e.g: {{#sed}}s/all/world/,helloall{{/sed}} */
Llamadas sed anidadas
Tuve la suerte de poder coincidir con @Disabled y @Test en una sola búsqueda y reemplazo.
Si el código es más complicado y quiero tener una secuencia de comandos sed entonces puedo hacerlo anidando:
{{#sed}}s/@Test//,{{#sed}}s/@Disabled\n//,{{{ modifierList }}}{{/sed}}{{/sed}}
En el ejemplo anterior, aplico el reemplazo @Test a los resultados de aplicar el reemplazo @Disabled en el {{{ modifierList }}}.
Resumen
sed es una forma muy flexible de lograr la reescritura de código y es posible anidar las llamadas a la función sed para obtener condiciones de reescritura complicadas.
Este tipo de recetas suelen ser temporales porque las utilizamos para mejorar nuestro proceso de programación, y una vez que hayamos creado la memoria muscular y ya no utilicemos el mal patrón de programación podemos eliminarlo o desactivarlo en el Libro de Cocina.
---
Puedes instalar Sensei desde IntelliJ usando "Preferences \ Plugins" (Mac) o "Settings \ Plugins" (Windows) y luego sólo busca "sensei secure code".
Todo el código de esta entrada del blog se puede encontrar en GitHub en el módulo `junitexamples` de nuestro repositorio de ejemplos del blog https://github.com/SecureCodeWarrior/sensei-blog-examples
Índice
Alan Richardson cuenta con más de veinte años de experiencia profesional en TI, trabajando como desarrollador y en todos los niveles de la jerarquía de pruebas, desde probador hasta jefe de pruebas. Jefe de Relaciones con los Desarrolladores en Secure Code Warrior, trabaja directamente con los equipos, para mejorar el desarrollo de un código seguro de calidad. Alan es autor de cuatro libros, entre ellos "Dear Evil Tester" y "Java For Testers". Alan también ha creado una formación en línea courses para ayudar a la gente a aprender las pruebas técnicas de la web y Selenium WebDriver con Java. Alan publica sus escritos y vídeos de formación en SeleniumSimplified.com, EvilTester.com, JavaForTesters.com, y CompendiumDev.co.uk.
Secure Code Warrior está a disposición de su organización para ayudarle a proteger el código a lo largo de todo el ciclo de vida de desarrollo de software y crear una cultura en la que la ciberseguridad sea una prioridad. Tanto si es director de AppSec, desarrollador, CISO o cualquier persona implicada en la seguridad, podemos ayudar a su organización a reducir los riesgos asociados a un código inseguro.
Reservar una demostraciónDescargarRecursos para empezar
Evaluación comparativa de las competencias en materia de seguridad: optimización del diseño seguro en la empresa
El movimiento Secure-by-Design es el futuro del desarrollo de software seguro. Conozca los elementos clave que las empresas deben tener en cuenta cuando piensan en una iniciativa Secure-by-Design.
DigitalOcean reduce su deuda de seguridad con Secure Code Warrior
El uso por parte de DigitalOcean de la formación Secure Code Warrior ha reducido significativamente la deuda de seguridad, permitiendo a los equipos centrarse más en la innovación y la productividad. La mejora de la seguridad ha reforzado la calidad de sus productos y su ventaja competitiva. De cara al futuro, SCW Trust Score les ayudará a seguir mejorando las prácticas de seguridad y a continuar impulsando la innovación.
Recursos para empezar
La puntuación de confianza revela el valor de las iniciativas de mejora de la seguridad mediante el diseño
Nuestra investigación ha demostrado que la formación en código seguro funciona. Trust Score, que utiliza un algoritmo basado en más de 20 millones de puntos de datos de aprendizaje procedentes del trabajo de más de 250 000 alumnos en más de 600 organizaciones, revela su eficacia a la hora de reducir las vulnerabilidades y cómo hacer que la iniciativa sea aún más eficaz.
Seguridad reactiva frente a seguridad preventiva: Prevenir es mejor que curar
La idea de introducir la seguridad preventiva en el código y los sistemas heredados al mismo tiempo que en las aplicaciones más recientes puede parecer desalentadora, pero un planteamiento basado en el diseño seguro, aplicado mediante la mejora de las competencias de los desarrolladores, puede aplicar las mejores prácticas de seguridad a esos sistemas. Es la mejor oportunidad que tienen muchas organizaciones de mejorar su seguridad.
Ventajas de la evaluación comparativa de las competencias de seguridad de los desarrolladores
La creciente atención que se presta al código seguro y a los principios del diseño seguro exige que los desarrolladores reciban formación en ciberseguridad desde el principio del proceso de desarrollo de software, con herramientas como Secure Code Warrior's Trust Score, que ayudan a medir y mejorar sus progresos.
Impulsando iniciativas de seguridad por diseño para empresas con éxito significativo
Nuestro último documento de investigación, Benchmarking Security Skills: Streamlining Secure-by-Design in the Enterprise, es el resultado de un análisis profundo de iniciativas reales de Secure-by-Design a nivel empresarial y de la derivación de enfoques de mejores prácticas basados en hallazgos basados en datos.