Modelos fuente de pruebas unitarias

10

Tengo varios modelos en mi extensión personalizada que solo sirven para completar algunas selecciones y / o multiselecciones en el formulario de agregar / editar de mis entidades.
Entonces son lo que magento llama "modelos fuente".
Los valores involucrados son siempre los mismos y los métodos devuelven lo mismo.
¿Cómo debo probar esos? O incluso mejor, ¿debería escribir pruebas unitarias para ellos?
Aquí hay un ejemplo.
La siguiente clase se utiliza para agregar / editar formulario para un campo llamado typey para la columna de cuadrícula del mismo campo.

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}
Marius
fuente

Respuestas:

15

Este es un gran hilo, y realmente me gustan las respuestas de @KAndy y @fschmengler.
Me gustaría agregar algunas ideas adicionales que considero valiosas cuando hago una pregunta como "¿Debería probar X?" o "¿Cómo debo probar X?".

¿Qué puede salir mal?

  • Podría hacer un error tipográfico tonto (sucede todo el tiempo)
    Esto generalmente no justifica escribir una prueba.
  • ¿Copiaré el código que necesito del núcleo o de un módulo diferente y luego lo ajustaré a mis necesidades?
    Creo que esto es realmente algo muy peligroso que a menudo deja errores sutiles. En este caso, estoy a favor de escribir una prueba, si no es demasiado costosa. Hacer que la configuración de los modelos de origen se base en realidad los haría una OMI más arriesgada.
  • ¿Puede haber un conflicto con un módulo diferente?
    Esto casi solo se aplica al código de configuración. En tal caso, me gustaría tener una prueba de integración que me diga cuándo sucede eso.
  • ¿Podría Magento cambiar la API en una versión futura?
    Muy poco probable en este caso, ya que su código solo depende de una interfaz. Pero las clases más concretas están involucradas o si mi código extiende una clase central, esto se convierte en un riesgo potencial.
  • Una nueva versión de PHP podría romper mi código. O tal vez quiero admitir PHP 5.6 en los próximos años.
    Una vez más, es altamente improbable aquí, pero en algunos casos quiero una prueba que me advierta, ¿debería cambiar el código en el futuro para usar una sintaxis incompatible?

¿Qué tan costoso es probar el código?

Esto tiene dos aspectos:

  • La cantidad de esfuerzo y tiempo que lleva escribir un examen
  • La cantidad de esfuerzo y tiempo que lleva probar el código que estoy a punto de escribir manualmente.

Durante el desarrollo de algún fragmento de código, tiendo a tener que ejecutar el código que escribo con bastante frecuencia hasta que lo considero hecho. Por supuesto, esto es mucho más fácil con una prueba unitaria.

En su caso, escribir un examen es muy barato. No toma mucho tiempo o esfuerzo. @KAndy tiene razón en que todo el código debe mantenerse, pero, de nuevo, no todas las pruebas deben mantenerse.
Este podría ser un ejemplo en el que escribiría una prueba unitaria, solo para verificar que no cometo un error tonto, y luego lo eliminaré una vez que la clase haya terminado. Si una prueba no proporciona un valor a largo plazo, creo que eliminarlas tiene sentido.

Esta pregunta también es importante en términos de elegir el tipo de prueba para escribir: unidad o integración, por ejemplo.

¿Qué valor tiene la pieza de código que estoy escribiendo?

Si un código que estoy escribiendo es esencial para el servicio que proporciona un módulo, lo pruebo, independientemente de lo trivial que sea.
Si es solo un pequeño método de utilidad, por ejemplo, centrado en la interfaz de usuario, y no forma parte de la lógica empresarial, entonces tal vez no.

¿Tendrá que cambiar el código?

Con el tiempo, me he acostumbrado tanto a tener una cobertura de prueba, que cambiar el código descubierto se siente muy inseguro. Eso incluye cosas tan simples como agregar una opción a un modelo fuente, pero también cosas como mover una clase a una carpeta / espacio de nombres diferente o cambiar el nombre de un método.
Tener pruebas para tales cambios es invaluable.

¿Necesita documentación?

¿Qué tan difícil es usar el código? En su ejemplo, es trivial, pero en algunos casos más complejos, tener una prueba es excelente para fines de documentación para otros desarrolladores (o para mí en unos pocos meses).

Exploración y Aprendizaje

Si estoy trabajando en algún código y no estoy seguro de cómo probarlo, me resulta muy valioso escribir una prueba. El proceso casi siempre me da una comprensión más profunda de lo que estoy tratando.
Esto es especialmente cierto para los desarrolladores que todavía se consideran a sí mismos aprendiendo pruebas.
Este también es un ejemplo en el que podría tener sentido eliminar la prueba después, porque el valor principal que proporcionó fue el aprendizaje.

Disciplina y estrés

Seguir el ciclo refactor rojo-verde me ayuda a ir rápido. Esto es especialmente cierto bajo presión. Entonces, incluso si algún fragmento de código no es realmente digno de prueba, aún podría seguir TDD, especialmente si el código es trivial para probar.
Esto me mantiene en el flujo y alerta.

¿Qué y cómo probar?

También considere que puede escribir la prueba en granularidad muy diferente.

  • Prueba del valor de retorno exacto.
    Esta será una prueba muy rígida que deberá ajustarse a cada cambio. ¿Desea que la prueba se rompa, por ejemplo, si cambia el orden de los elementos en la matriz de retorno?
  • Probar la estructura del valor de retorno.
    Para el modelo fuente, esto podría ser verificar cada sub-matriz como dos registros, uno con una labely otro con una valueclave.
  • Comprobando los implementos de la clase ArrayInterface.
  • Probar la clase proporciona getOptions()aunque ese método no sea parte de la interfaz implementada.

Para cada cosa posible que se pueda probar, considere el valor, la mantenibilidad y el costo.

Resumen

Para resumir: no hay una respuesta única verdadera a una pregunta si alguna pieza de código debe probarse o no. La respuesta será diferente para cada desarrollador dependiendo de las circunstancias.

Vinaí
fuente
2
¡Gran respuesta! Quiero resaltar la parte de "eliminar pruebas cuando ya no proporcionan valor", a veces las pruebas antiguas que ayudaron durante el desarrollo inicial, solo se están interponiendo a largo plazo.
Fabian Schmengler
1
acabas de responder otras 2 preguntas sobre las que tenía dudas. gracias
Marius
6

En mi opinión, no hay una respuesta general a "escribir pruebas unitarias para modelos fuente, sí o no"

He escrito pruebas unitarias para modelos de origen, pero esos eran modelos de origen dinámico que obtuvieron datos externos y allí tiene mucho sentido.

Para los modelos fuente que no son más que matrices glorificadas (como en su ejemplo), no me molestaría en escribir pruebas unitarias. Pero de alguna manera, debe asegurarse de no cometer un error. Hay varias opciones:

  1. prueba de unidad
  2. examen de integración
  3. mira manualmente la configuración

¿Estás siguiendo TDD? Luego elija entre (1) y (2), o ambos. De lo contrario, elija entre (2) y (3).


Si utiliza los modelos de origen para las opciones de configuración del sistema, una prueba de integración (una prueba para todas sus opciones de configuración) podría representar la sección de configuración y verificar si los <select>elementos están presentes y contienen los valores esperados. Recuerda que no se trata de probar una clase en particular, sino de comprobar que todo está unido correctamente.


Y como dijo @KAndy, idealmente no necesitaría tanta repetitiva, solo extienda una clase base que ya esté probada por la unidad y anule una propiedad o defina los valores en una configuración externa.

En ese escenario, las pruebas unitarias para implementaciones concretas no proporcionan ningún valor adicional. Si tiene muchas de estas clases, puede ser una buena idea escribir una clase base ArraySourceusted mismo, siempre que Magento no la proporcione.


Con tal clase base, su modelo fuente podría verse así:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}
Fabian Schmengler
fuente
Gracias por la confirmación. Intentaré convertir mis matrices glorificadas en una lista de opciones configurables, pero ¿se aplica lo mismo para los modelos que tendrán exactamente N opciones? Como un modelo fuente de "estado".
Marius
No veo cómo un modelo fuente de "estado" sería diferente a su ejemplo de "tipo de autor"
Fabian Schmengler
porque podría usar los valores 0 y 1 (como constantes de clase) en la interfaz para filtrar, por ejemplo, las entidades habilitadas. Quiero decir, en este caso, los valores de las opciones tienen lógica detrás de ellos. No son solo key=>valueparejas.
Marius
Pero esta lógica no es parte del modelo fuente, ¿verdad? Eso significa que no importa aquí. Aún tendrás las constantes para usar en otros lugares. Agregué un ejemplo de cómo usaría tal implementación.
Fabian Schmengler
5

¿Cómo debo probar esos?

Creo que no deberías

Agregue código al sistema para aumentar el costo de soporte y mantenimiento, pero el proceso de prueba debe ser LEAN .

Más sobre este código no debería existir. Creo que en Magento debería haber una forma declarativa genérica de definir conjuntos de opciones para usar en diferentes lugares. Y tu reticencia a la prueba de escritura para esta clase me muestra olor a código malo

Kandy
fuente
1
Gracias. Entonces está diciendo que estas opciones no deberían estar codificadas sino que provienen de un archivo de configuración (por ejemplo, di.xml). Yo puedo hacer eso. ¿Pero es lo mismo para los modelos de fuente de estado? Quiero decir, si tengo la misma clase que la anterior pero solo con el estado habilitado y deshabilitado (1,0) ¿debo usar el mismo enfoque de configuración? En caso afirmativo, quiero decir que me parece una ingeniería excesiva.
Marius