Phpunit: Los proveedores de datos se ejecutan antes de setupBeforeClass

Creado en 21 feb. 2013  ·  21Comentarios  ·  Fuente: sebastianbergmann/phpunit

Se llama a los proveedores de datos antes de que se ejecute el setupBeforeClass estático. Si creo que debería ser al revés.

Caso de uso:

  • Tenemos una lista de adaptadores. Algunos adaptadores solo son compatibles con algunos entornos (es decir, Linux vs Win),
  • Me gustaría inicializar una lista de adaptadores compatibles una vez y pasarla a todas las pruebas a través de un proveedor de datos.
  • La forma más limpia que puedo imaginar es inicializar una propiedad estática en setupBeforeClass y hacer que el proveedor devuelva esa propiedad

El problema es que la propiedad no se inicializa cuando se llama al proveedor.

Versión encontrada: 3.7.14

Comentario más útil

@epdenouden JIT? ¿Está probando con JIT para PHP o hay algo que no sé? Lo siento, estoy un poco confundido; ¿Estás hablando de PHP7.xy PHP8.x o PHPUnit7.xy PHPUnit8.x?

@MAChitgarha Entiendo su confusión ya que PHP8 está obteniendo un _compiler_ justo a tiempo. Estaba usando el término para la implementación del nuevo

Esto funcionará bien con la próxima versión de PHP 8 JIT, ya que no utiliza ninguna característica complicada del lenguaje PHP en sí. Todo mi trabajo está en lo más profundo de PHPUnit rehaciendo cosas como el análisis de la configuración, la carga y ejecución de pruebas, etc.

Si parece que no estoy haciendo nada, lo estoy haciendo bien. Excepto por esa factura de GCP o AWS más baja. 💸

Todos 21 comentarios

No estoy seguro de que aplicar algún orden para que los recursos estáticos se puedan inicializar en setupBeforeClass realmente tenga sentido si considera que los proveedores de datos no deben ser estáticos o incluso en la misma clase de prueba.

¿Por qué no inicializar los adaptadores de forma perezosa? Entonces el orden no importa en absoluto.

Sin ejecutar el proveedor de datos, no sabemos cuántas pruebas ejecuta un método de prueba. En un mundo perfecto, romperíamos B / C y requeriríamos que los proveedores de datos sean objetos que implementen una interfaz que se extienda Countable . De esa manera podríamos separar estas preocupaciones y tener una situación mucho más limpia.

@whatthejeff Esto es lo que terminamos haciendo. Sin embargo, la inicialización en el constructor podría ser un poco más limpia, porque los parámetros deben estar envueltos en una matriz cuando los devuelve un proveedor.

@sebastianbergmann ok, entiendo por qué se les llama antes, ¿qué tal si lo mencionas en el documento? (Puedo abrir un PR que cambia si está bien). Ni siquiera estoy seguro de que tener objetos ayude mucho: si miras a mi ex, hay una dependencia de un proveedor a otro.

Gracias por sus respuestas, cerrando el problema.

@vicb , con mucho gusto fusionaré un PR relacionado con la documentación para esto.

Puede que llegue un poco tarde a la escena, pero lo que hago cuando tengo dependencias que necesito inicializar es deshacerme de la anotación @dataProvider y usar yield lugar.

@srosato estoy siendo tonto (no he usado rendimiento). ¿Podría dar un breve ejemplo de cómo hacer esto, por favor? A través de https://gist.github.com si un ejemplo aquí no es apropiado.

Si su @dataProvider requiere un código de configuración y no son demasiado grandes, puede especificarlos como cadenas y eval en la prueba. Por ejemplo:

public function myProvider() {
    return [
        'new Klass("param 1", "param 2")',
        'new Klass("param 1", "param 2")',
    ];
}

/**
 * <strong i="8">@dataProvider</strong> myProvider
 */
public testMyFunction($instance_str) {
    $klass = eval("return {$instance_str};");
    # continue testing $klass ...
}

Mis dos centavos:

En lugar de que un proveedor de datos se base en una propiedad estática (que sabemos que no es posible), estoy configurando los datos / objetos necesarios como miembros de clase de la clase de prueba en el método setUp() .
en tearDown() configuro en nulo, o cuando hay objetos complejos implemento un método clear() para ello.

Siempre que no haya demasiadas dependencias en toda la clase de prueba, me siento cómodo con este enfoque.
pero cuando hay más de una o dos dependencias de este tipo, es posible que deba pensar demasiado en su diseño general.

específicamente, utilizo una conexión de base de datos como propiedad estática que se establece en setUpBeforeClass() y se establece $queryBuilder , que obtiene la conexión inyectada, como miembro de clase en setUp() (en lugar de devolviéndolo en un proveedor de datos).

Una solución sería definir un nuevo método privado y definir tantas variables como sea necesario, como static dentro de él. Luego, verifique si uno de ellos está configurado, y si no lo está, configúrelos todos y devuélvalos (o ceda). De esta manera, _las variables se declararán solo una vez_, incluso si tiene diez proveedores o lo que sea. Además, puedes usarlo en setUpBeforeClass() o setUp() .

Véalo en un ejemplo:

private static function getData()
{
    static $data, $anotherData;

    if (!isset($data)) {
        $data = new TestClass();
        $anotherData = [];
    }

    return [
        $data,
        // Or: clone $data
        $anotherData,
    ];
}

public static function setUpBeforeClass()
{
    list(self::$sampleJson, self::$sampleData) = self::getData(); 
}

public function sampleProvider()
{
    $data = self::getData();

    return [
        $data
    ];
}

@MAChitgarha gracias a tu comentario de ayer me enteré de esta entrada. :)

Estoy trabajando en refactorizar la lógica del proveedor de datos en el n. ° 3736, lo que resolverá algunos de los problemas comunes:

  • ya no tiene este gran aumento en la actividad al comienzo de la ejecución, ya que todos los proveedores de datos se cargan
  • los proveedores de datos pueden ejecutarse después de los accesorios setUpBeforeClass y setUp
  • los proveedores de datos no estáticos se vuelven posibles
  • los generadores serán mucho más eficientes

@epdenouden ¡ Feliz de escuchar eso! ;) El segundo elemento de su lista es bueno. Esperando eso.

Los proveedores de datos no estáticos se vuelven posibles

¿No es posible actualmente? Siempre uso proveedores de datos no estáticos en mis pruebas en lugar de estáticos, y funcionan como se esperaba sin problemas (usando PHPUnit 7.5.6). ¿Me equivoco?

¿No es posible actualmente? Siempre uso proveedores de datos no estáticos en mis pruebas en lugar de estáticos, y funcionan como se esperaba sin problemas (usando PHPUnit 7.5.6). ¿Me equivoco?

¡No, tienes razón! Mi fraseo no fue lo suficientemente precisa, gracias por mencionar esto. En el fondo del código de manejo del proveedor de datos, se lee actualmente:

private static function getDataFromDataProviderAnnotation($allTheParameters): ?iterable
    // code for locating the data provider
    // [...]
                if ($dataProviderMethod->isStatic()) {
                    $object = null;
                } else {
                    $object = $dataProviderClass->newInstance();
                }

    // code for preparing returned data rows
    // [...]
}

Así que aquí está el pequeño y sucio secreto:

  • puede utilizar proveedores de datos no estáticos y
  • se llamarán en un instanceof real pero ...
  • _no es la misma instancia_ que se usa para los accesorios y las pruebas

¡Me diste algo para pensar y validar con respecto a la refactorización del proveedor de datos! Necesito agregar pruebas adicionales para asegurarme de que las instancias del objeto sean las esperadas, no solo del mismo tipo o un clon. ☕️ y 🍰 conmigo cuando estés en Ámsterdam.

@epdenouden ¡ Pero no tengo fallas! En PHPUnit 7.5.13, la aserción no genera ningún error. ¿Cómo se obtiene este error? ¿Incompatibilidad hacia atrás, quieres decir?

Esto está en la rama del proveedor de datos de carga diferida en la que estoy trabajando, que se basa en 8.2. Verificaré 7.5.x a continuación, gracias por el aviso.

Si esto funciona, tendré que volver a algunos otros problemas anteriores que pedían explícitamente implementar métodos de proveedor no estáticos y volver a ver los casos de uso exactos.

En cualquier caso: tu comentario ya ha inspirado una prueba muy útil :)

Y @MAChitgarha sí, eso sería un descanso de BC

@epdenouden , esperaré por eso. Por algunas razones, en mi proyecto, estoy usando PHPUnit 7.5.13; pero lo actualizaré a 8.2. * pronto. Y buenas noticias, si el BC-break se pudiera arreglar. No conozco la estructura del PHPUnit en sí, así que no sé qué cambió, pero supongo que es posible arreglarlo. Sin embargo, ¡todo depende de ti! :)

Y buenas noticias, si el BC-break se pudiera arreglar. No conozco la estructura del PHPUnit en sí, así que no sé qué cambió, pero supongo que es posible arreglarlo. Sin embargo, ¡todo depende de ti! :)

Introducir una ruptura de compatibilidad con versiones anteriores sin una muy buena razón no es algo que @sebastianbergmann permitiría, por lo que se solucionará antes de pasar a la próxima versión. PHPUnit es una herramienta que se utiliza para proteger la calidad del software en la comunidad PHP y no en mi campo de juego personal.

Entonces, sí, díganos qué le gustaría ver como usuario final / desarrollador de pruebas. Solo tenga en cuenta que proyectos como estos no tienen una gran cantidad de desarrolladores voluntarios.

Dato curioso: la prueba anterior funciona en las versiones 7.xy 8.xy falla en el prototipo JIT.

El código en el que estoy trabajando se parece mucho más al flujo del código original. Por lo tanto, podría ser el resultado de un error, algo de lógica que todavía necesito refactorizar más, algo de bajo nivel sobre los objetos y la reflexión en PHP que necesito buscar o tal vez solo algunas pruebas ingenuas de mi parte. 🔬

@epdenouden JIT? ¿Está probando con JIT para PHP o hay algo que no sé? Lo siento, estoy un poco confundido; ¿Estás hablando de PHP7.xy PHP8.x o PHPUnit7.xy PHPUnit8.x?

@epdenouden JIT? ¿Está probando con JIT para PHP o hay algo que no sé? Lo siento, estoy un poco confundido; ¿Estás hablando de PHP7.xy PHP8.x o PHPUnit7.xy PHPUnit8.x?

@MAChitgarha Entiendo su confusión ya que PHP8 está obteniendo un _compiler_ justo a tiempo. Estaba usando el término para la implementación del nuevo

Esto funcionará bien con la próxima versión de PHP 8 JIT, ya que no utiliza ninguna característica complicada del lenguaje PHP en sí. Todo mi trabajo está en lo más profundo de PHPUnit rehaciendo cosas como el análisis de la configuración, la carga y ejecución de pruebas, etc.

Si parece que no estoy haciendo nada, lo estoy haciendo bien. Excepto por esa factura de GCP o AWS más baja. 💸

¿Fue útil esta página
0 / 5 - 0 calificaciones