paint-brush
Pruebas en Godot: cómo las abordo personalmentepor@dlowl
725 lecturas
725 lecturas

Pruebas en Godot: cómo las abordo personalmente

por D. Lowl4m2024/03/07
Read on Terminal Reader

Demasiado Largo; Para Leer

Godot Unit Test (GUT) se puede instalar desde AssetLib con un solo clic. Proporciona una clase que podemos ampliar para nuestros scripts de prueba. Tiene una interfaz de usuario agradable con la capacidad de utilizar el depurador de Godot y ejecutar pruebas individuales. Se puede ejecutar desde CLI y en CI (pero me ocuparé de esto más adelante)
featured image - Pruebas en Godot: cómo las abordo personalmente
D. Lowl HackerNoon profile picture

Para cambiar de ritmo, me gustaría hacer un pequeño registro de desarrollo. Hace algún tiempo, participé en un game jam e hice este juego, Of Mice and Bad Choices , un juego de rompecabezas corto en el que colocas queso alrededor del laberinto para atraer a los ratones. Fue divertido, pero evidentemente hubo algunas deficiencias.


Uno de los principales es que el comportamiento de los ratones no es intuitivo. Los jugadores mencionaron que esperarían que los ratones se sintieran repelidos por un queso que no les gusta, no solo por congelarse. Además, implementar esta mecánica permitiría un diseño de rompecabezas mucho más rico.


Entonces, creo que esta es una buena oportunidad para realizar pruebas automatizadas en Godot.

Un ejemplo de un comportamiento esperado: el ratón debería alejarse del queso azul

Herramientas de prueba

Hay algunos marcos de prueba disponibles para Godot 4, pero el que me llamó la atención es Godot Unit Test (GUT) . GUT es bastante simple:


  • Se puede instalar desde AssetLib con un solo clic.


  • Proporciona una clase que podemos ampliar para nuestros scripts de prueba: simplemente agregue funciones que comiencen con test_ y escriba algunas afirmaciones (estructura típica de prueba unitaria).


  • Tiene una interfaz de usuario agradable con la capacidad de utilizar el depurador de Godot y ejecutar pruebas individuales.


  • Se puede ejecutar desde CLI y en CI (pero me ocuparé de esto más adelante).

Mi marco de prueba

Para este caso particular, quería tener una forma de definir escenarios complejos, de la misma manera que defino los niveles para el juego: en el editor del motor en lugar de en el código (de esta manera, las pruebas estarían más cerca de la realidad). Por eso quiero hacer estas cosas:


  • Tener una función de corredor único que toma un mapa y ejecuta las pruebas.


  • Tener una colección de mapas, cada uno con un conjunto de escenarios (casos de prueba) para ejecutar.


  • Tenga una forma de definir casos de prueba arrastrando y soltando: coloque un mouse y establezca dónde debería estar en N vueltas.


Entonces, desenvolvamos esto.

Definiciones de casos de prueba

Definamos una nueva clase `MouseTestCase. Queremos que herede Node2D (ya que queremos colocarlo en una escena. Y queremos que encuentre uno de sus hijos (que nosotros mismos colocaremos en una escena): un mouse y su posición final esperada (como Marcador)

 extends Node2D class_name MouseTestCase @export var steps_left = 0 # How many steps to simulate @export var done = false @onready var mouse: Mouse = $Mouse @onready var expected_position = SnapUtils.get_tile_map_position($TestMarker.position)


Ahora podemos ponerlo en escena y ¡estamos bien! Sabemos dónde comienza un mouse, sabemos dónde debe terminar y en cuántos pasos.

Un árbol de nodos para definir un caso de prueba.

Así se ve en el mapa: el ratón a probar en verde, el marcador de destino en rojo

Mapas de prueba

Ahora, hagamos muchos más y hagamos un mapa para probar nuestro comportamiento repelente.

El mapa de pruebas resultante para las pruebas mecánicas de "repulsión"

Este comportamiento es algo complejo, por lo que queremos cubrir muchos casos ligeramente diferentes:

  • Un ratón quiere alejarse del queso que no le gusta/


  • Un ratón quiere mantener la dirección del movimiento (es decir, evita giros)


  • Un ratón prefiere los giros de izquierda a derecha y los giros en U.


El mapa resultante que define 12 casos de prueba para cubrir este comportamiento se muestra arriba (imagínese lo tedioso que podría ser codificar todas esas coordenadas en código).

Corredor de pruebas

Lo único que queda por hacer es la función de ejecución de pruebas. La función necesita:

  • Cargue el mapa que hemos definido anteriormente.


  • Simule los pasos del juego hacia adelante hasta que se completen todos los casos de prueba.


  • En cada paso, repita todos los casos de prueba y, si están terminados, verifique si se alcanza la posición esperada.


El código es bastante simple.

 func run_level_with_mouse_test_cases(map_path: String): var level = load(map_path) map.load_level(level) var cases = MouseTestCase.cast_all_cases(get_tree().get_nodes_in_group(MouseTestCase.MTC_GROUP_NAME)) while (cases.any(func(case): return not case.done)): map.move_mice() for case in cases: if not case.done: case.steps_left -= 1 if case.steps_left == 0: case.done = true assert_eq(case.get_mouse_position(), case.expected_position, case.get_parent().name+"/"+case.name)

Me imagino que esto evolucionará, pero la implementación actual es lo suficientemente buena por ahora. ¡Escribí las pruebas, implementé la mecánica y las pruebas realmente confirman que la mecánica está implementada correctamente!

El panel de GUT que muestra la ejecución de prueba exitosa

Discusiones

Aquí, he mostrado una forma de abordar las pruebas en los juegos. Obviamente, hay muchas más cosas que mejorar aquí, y animo a los lectores a tomar el código y el marco y adaptarlos a sus necesidades.


Como siempre, el código está disponible en GitHub: https://github.com/d-lowl/of-mice-and-bad-choices También puede echar un vistazo al PR específico que presenta las pruebas. Para obtener puntos de bonificación, sería genial si alguien pudiera hacerlos funcionar en CI. Salud.