Acerca de las pruebas de consultas personalizadas
CodeQL proporciona un marco de pruebas sencillo para pruebas de regresión automatizadas de consultas. Prueba las consultas para asegurarte de que se comportan según lo previsto.
Durante una prueba de consulta, CodeQL compara los resultados que el usuario prevé que la consulta genere con los producidos realmente. Si los resultados esperados y los reales difieren, se produce un error en la prueba de la consulta. Para corregir la prueba, debe recorrer en iteración la consulta y los resultados esperados hasta que los resultados reales y los esperados coincidan totalmente. En este tema se muestra cómo crear archivos de prueba y ejecutar pruebas en ellos mediante el subcomando test run
.
Configuración de un paquete de pruebas de CodeQL para consultas personalizadas
Todas las pruebas de CodeQL deben almacenarse en un paquete especial de "prueba" de CodeQL. Es decir, un directorio para los archivos de prueba con un archivo qlpack.yml
que define lo siguiente:
name: <name-of-test-pack>
version: 0.0.0
dependencies:
<codeql-libraries-and-queries-to-test>: "*"
extractor: <language-of-code-to-test>
El valor dependencies
especifica los paquetes de CodeQL que contienen consultas que se van a probar.
Normalmente, estos paquetes se resolverán desde el origen, por lo que no es necesario especificar una versión fija del paquete. extractor
define qué lenguaje usará la CLI para crear bases de datos de prueba a partir de los archivos de código almacenados en este paquete de CodeQL. Para más información, consulta Personalización del análisis con paquetes de CodeQL.
Es posible que le resulte útil examinar la forma en que se organizan las pruebas de consulta en el repositorio de CodeQL. Cada lenguaje tiene un directorio src
, ql/<language>/ql/src
, que contiene bibliotecas y consultas para analizar los códigos base. Junto con el directorio src
, hay otro test
con pruebas para estas bibliotecas y consultas.
Cada directorio test
se configura como un paquete de prueba de CodeQL con dos subdirectorios:
query-tests
es una serie de subdirectorios con pruebas para consultas almacenadas en el directoriosrc
. Cada subdirectorio contiene código de prueba y un archivo de referencia QL que especifica la consulta que se va a probar.library-tests
es una serie de subdirectorios con pruebas para archivos de biblioteca QL. Cada subdirectorio contiene código de prueba y consultas que se escribieron como pruebas unitarias para una biblioteca.
Después de crear el archivo qlpack.yml
, debes asegurarte de que todas las dependencias se descarguen y estén disponibles para la CLI. Para ello, ejecute el siguiente comando en el mismo directorio que el archivo qlpack.yml
:
codeql pack install
Esto generará un archivo codeql-pack.lock.yml
que especifica todas las dependencias transitivas necesarias para ejecutar consultas en este paquete. Este archivo debe estar registrado en el control de código fuente.
Configuración de los archivos de prueba para una consulta
Para cada consulta que quieras probar, debes crear un subdirectorio en el paquete de prueba de CodeQL. Después, agrega los archivos siguientes al subdirectorio antes de ejecutar el comando de prueba:
-
Un archivo de referencia de consulta (archivo
.qlref
) que define la ubicación de la consulta que se va a probar. La ubicación se define en relación con la raíz del paquete de CodeQL que contiene la consulta. Normalmente, se trata de un paquete de CodeQL especificado en el bloquedependencies
del paquete de pruebas. Para más información, consulta Archivos de referencia de consulta.No es necesario agregar un archivo de referencia de consulta si la consulta que quieres probar se almacena en el directorio de prueba, pero por lo general es recomendable almacenar consultas independientemente de las pruebas. La única excepción son las pruebas unitarias para las bibliotecas de QL, que tienden a almacenarse en paquetes de prueba, independientes de las consultas que generan alertas o rutas de acceso.
-
El código de ejemplo en el que quieres ejecutar la consulta. Esto debe estar compuesto de uno o varios archivos que contengan ejemplos del código que la consulta esté diseñada para identificarse.
También puedes definir los resultados que esperas ver al ejecutar la consulta en el código de ejemplo; para ello, crea un archivo con la extensión .expected
. Como alternativa, puedes dejar el comando de prueba para crear el archivo .expected
automáticamente.
En el ejemplo siguiente se muestra cómo crear y probar una consulta.
Note
Los archivos .ql
, .qlref
y .expected
deben tener nombres coherentes:
- Si quieres especificar directamente el propio archivo
.ql
en el comando de prueba, debe tener el mismo nombre base que el archivo.expected
correspondiente. Por ejemplo, si la consulta esMyJavaQuery.ql
, el archivo de resultados esperado debe serMyJavaQuery.expected
. - Si quieres especificar un archivo
.qlref
en el comando, debe tener el mismo nombre base que el archivo.expected
correspondiente, pero la propia consulta puede tener un nombre diferente. - Los nombres de los archivos de código de ejemplo no tienen que ser coherentes con los demás archivos de prueba. Todos los archivos de código de ejemplo encontrados junto al archivo
.qlref
(o.ql
) y en cualquier subdirectorio se usarán para crear una base de datos de prueba. Por lo tanto, por motivos de simplicidad, se recomienda no guardar archivos de prueba en directorios que sean predecesores entre sí.
En ejecución codeql test run
Para ejecutar las pruebas de consulta de CodeQL, ejecute el comando siguiente:
codeql test run <test|dir>
El argumento <test|dir>
puede ser uno de los siguientes:
- Ruta de acceso a un archivo
.ql
. - Ruta de acceso a un archivo
.qlref
que haga referencia a un archivo.ql
. - Ruta de acceso a un directorio en el que se buscarán de forma recursiva los archivos
.ql
y.qlref
.
También puede especificar:
--threads:
de forma opcional: cantidad de subprocesos que se van a usar cuando se ejecutan las consultas. La opción predeterminada es1
. Puede especificar más subprocesos para acelerar la ejecución de consultas. La especificación de0
hace coincidir el número de subprocesos con el número de procesadores lógicos.
Para obtener detalles completos de todas las opciones que puedes usar al probar consultas, consulta test run.
Ejemplo
En el ejemplo siguiente se muestra cómo configurar una prueba para una consulta que busca código Java para instrucciones if
que tienen bloques then
vacíos. Incluye pasos para agregar la consulta personalizada y los archivos de prueba correspondientes a fin de separar los paquetes de CodeQL fuera de la restauración del repositorio de CodeQL. Esto garantiza que, al actualizar las bibliotecas de CodeQL o restaurar una rama diferente, no sobrescribirás las consultas y pruebas personalizadas.
Preparación de una consulta y archivos de prueba
-
Desarrolla la consulta. Por ejemplo, la siguiente consulta sencilla busca bloques
then
vacíos en el código de Java:import java from IfStmt ifstmt where ifstmt.getThen() instanceof EmptyStmt select ifstmt, "This if statement has an empty then."
-
Guarda la consulta en un archivo denominado
EmptyThen.ql
en un directorio con las demás consultas personalizadas. Por ejemplo,custom-queries/java/queries/EmptyThen.ql
. -
Si aún no has agregado las consultas personalizadas a un paquete de CodeQL, crea ahora un paquete de CodeQL. Por ejemplo, si las consultas de Java personalizadas se almacenan en
custom-queries/java/queries
, agrega un archivoqlpack.yml
con el contenido siguiente acustom-queries/java/queries
:name: my-custom-queries dependencies: codeql/java-queries: "*"
Para obtener más información sobre los paquetes de CodeQL, consulta Personalización del análisis con paquetes de CodeQL.
-
Crea un paquete de CodeQL para las pruebas de Java agregando un archivo
qlpack.yml
con el siguiente contenido acustom-queries/java/tests
. De este modo, se actualizadependencies
para que coincida con el nombre del paquete de CodeQL de consultas personalizadas:El siguiente archivo
qlpack.yml
indica quemy-github-user/my-query-tests
depende demy-github-user/my-custom-queries
una versión mayor o igual que 1.2.3 y menor que 2.0.0. También declara que la CLI debe usar el extractor (extractor
) de Java al crear bases de datos de prueba. La líneatests: .
declara que todos los archivos.ql
del paquete se deben ejecutar como pruebas cuandocodeql test run
se ejecuta con la opción--strict-test-discovery
. Normalmente, los paquetes de prueba no contienen una propiedadversion
. Esto evita que se publiquen accidentalmente.name: my-github-user/my-query-tests dependencies: my-github-user/my-custom-queries: ^1.2.3 extractor: java-kotlin tests: .
-
Ejecuta
codeql pack install
en la raíz del directorio de prueba. Esto genera un archivocodeql-pack.lock.yml
que especifica todas las dependencias transitivas necesarias para ejecutar consultas en este paquete. -
En el paquete de pruebas de Java, crea un directorio para que contenga los archivos de prueba asociados a
EmptyThen.ql
. Por ejemplo,custom-queries/java/tests/EmptyThen
. -
En el directorio nuevo, crea
EmptyThen.qlref
para definir la ubicación deEmptyThen.ql
. La ruta de acceso a la consulta debe especificarse en relación con la raíz del paquete de CodeQL que contiene la consulta. En este caso, la consulta se encuentra en el directorio de nivel superior del paquete de CodeQL denominadomy-custom-queries
, que se declara como una dependencia demy-query-tests
. Por lo tanto,EmptyThen.qlref
simplemente debe contenerEmptyThen.ql
. -
Crea un fragmento de código para probarlo. El código de Java siguiente contiene una instrucción
if
vacía en la tercera línea. Guárdalo encustom-queries/java/tests/EmptyThen/Test.java
.class Test { public void problem(String arg) { if (arg.isEmpty()) ; { System.out.println("Empty argument"); } } public void good(String arg) { if (arg.isEmpty()) { System.out.println("Empty argument"); } } }
Ejecución de la prueba
Para ejecutar la prueba, muévela al directorio custom-queries
y ejecuta codeql test run java/tests/EmptyThen
.
Cuando se ejecuta la prueba, hace lo siguiente:
-
Buscar una prueba en el directorio
EmptyThen
. -
Extraer una base de datos de CodeQL de los archivos
.java
almacenados en el directorioEmptyThen
. -
Compilar la consulta a la que hace referencia el archivo
EmptyThen.qlref
.Si se produce un error en este paso, se debe a que la CLI no encuentra el paquete personalizado de CodeQL. Vuelve a ejecutar el comando y especifica la ubicación del paquete personalizado de CodeQL como, por ejemplo:
codeql test run --search-path=java java/tests/EmptyThen
Para obtener información sobre cómo guardar la ruta de acceso de búsqueda como parte de la configuración, consulta Especificación de opciones de comando en un archivo de configuración de CodeQL.
-
Ejecutar la prueba ejecutando la consulta y generando un archivo de resultados
EmptyThen.actual
. -
Comprobar si hay un archivo
EmptyThen.expected
que se va a comparar con el archivo de resultados.actual
. -
Notificar los resultados de la prueba; en este caso, un error:
0 tests passed; 1 tests failed:
. La prueba ha producido un error porque aún no hemos agregado un archivo con los resultados esperados de la consulta.
Visualización de la salida de la prueba de consulta
CodeQL genera los archivos siguientes en el directorio EmptyThen
:
EmptyThen.actual
, un archivo que contiene los resultados reales que ha generado la consulta.EmptyThen.testproj
, una base de datos de prueba que puedes cargar en VS Code y usar para depurar pruebas con errores. Cuando las pruebas se completan correctamente, esta base de datos se elimina en un paso de mantenimiento. Para invalidar este paso, ejecutatest run
con la opción--keep-databases
.
En este caso, el error estaba previsto y es fácil de corregir. Si abres el archivo EmptyThen.actual
, podrás ver los resultados de la prueba:
| Test.java:3:5:3:22 | stmt | This if statement has an empty then. |
Este archivo contiene una tabla, con una columna para la ubicación del resultado, junto con columnas independientes para cada parte de la cláusula select
que genera la consulta.
Dado que los resultados son los esperados, podemos actualizar la extensión de archivo a fin de definir esto como resultado esperado para esta prueba (EmptyThen.expected
).
Si vuelves a ejecutar la prueba ahora, la salida será similar, pero finalizará notificando All 1 tests passed.
.
Si los resultados de la consulta cambian, por ejemplo, si revisas la instrucción select
de la consulta, se producirá un error en la prueba. En el caso de los resultados con errores, la salida de la CLI incluye una diferencia unificada de los archivos EmptyThen.expected
y EmptyThen.actual
.
Esta información puede ser suficiente para depurar errores de prueba triviales.
Para los errores que son más difíciles de depurar, puedes importar EmptyThen.testproj
en CodeQL para VS Code, ejecutar EmptyThen.ql
y ver los resultados en el código de ejemplo Test.java
. Para más información, consulta Administración de bases de datos de CodeQL.