Jest: Contexto legible por humanos para las expectativas

Creado en 21 oct. 2016  ·  76Comentarios  ·  Fuente: facebook/jest

Si hay múltiples expectativas en un solo it , actualmente parece imposible determinar qué expectativa realmente falló sin hacer una referencia cruzada de la falla con los números de línea en su código.

test('api works', () => {
    expect(api()).toEqual([]) // api without magic provides no items
    expect(api(0)).toEqual([]) // api with zero magic also provides no items
    expect(api(true)).toEqual([1,2,3]) // api with magic enabled provides all items
})

¿Qué expectativa falló? ¿La primera o la segunda?

image

Sería bueno si hubiera algún contexto legible por humanos que aclarara de inmediato qué expectativa falló y qué significa realmente la salida de la expectativa en términos humanos, sin tener que encontrar el número de línea en la parte superior del seguimiento de la pila y mapearlo de nuevo a la código.


Compare el tape a continuación. Ignore que la cinta no se recupera después del primer error de afirmación. tape imprime un mensaje legible por humanos encima de cada falla de expectativa, lo que le permite saber exactamente qué prueba falló sin tener que volver al archivo de prueba.

Tenga en cuenta que esto también empuja el ruido legible por humanos hasta el final de la línea en la fuente de prueba, donde podría escribir un comentario de todos modos.

test('api works', t => {
  t.deepEquals(api(), [], 'api without magic provides no items')
  t.deepEquals(api(0), [], 'api with zero magic also provides no items')
  t.deepEquals(api(true), [1,2,3], 'api with magic enabled provides all items')
})

image


Parece que la única forma de adjuntar información legible por humanos a los errores con jest es envolver todo en un it adicional que es innecesariamente detallado en mi opinión.

describe('api works', () => {
  test('api without magic provides no items', () => {
    expect(api()).toEqual([])
  })
  test('api with zero magic also provides no items', () => {
    expect(api(0)).toEqual([])
  })
  test('api with magic enabled provides all items', () => {
    expect(api(true)).toEqual([1,2,3])
  })
})

Idealmente, uno podría adjuntar algún contexto legible por humanos al final de expect alguna manera.

p.ej

Mensaje de contexto como parámetro opcional adicional para métodos de aserción:

test('api works', () => {
    expect(api()).toEqual([], 'api without magic provides no items')
    expect(api(0)).toEqual([], 'api with zero magic provides no items')
    expect(api(true)).toEqual([1,2,3], 'api with magic enabled provides all items')
})


O mensaje de contexto como .because encadenado o .why o .comment o .t o algo así:

test('api works', () => {
    expect(api()).toEqual([]).because('api without magic provides no items')
    expect(api(0)).toEqual([]).because('api with zero magic provides no items')
    expect(api(true)).toEqual([1,2,3]).because('api with magic enabled provides all items')
})

Alternativamente, sería incluso mejor si jest simplemente pudiera leer el archivo e imprimir la línea de código fuente real en la que se encuentra la expectativa.

Comentario más útil

A partir de esta discusión y este repositorio , creo que uno agradable y semántico sería:

it('has all the methods', () => {
  since('cookie is a method').expect(reply.cookie).toBeDefined();
  since('download is a method').expect(reply.download).toBeDefined();
  since('end is a method').expect(reply.end).toBeDefined();
  // ...
});

El uso es similar al de because , pero tiene más sentido semánticamente.

Si le gusta esto, podría elaborar un PR agregando la funcionalidad since .

Todos 76 comentarios

¡Oye! Entonces, en realidad solíamos tener esto en Jasmine, pero descubrimos que en miles de archivos de prueba en FB, nadie lo usó. Entonces, por ahora, estamos imprimiendo un buen mensaje de error con información aproximada y un seguimiento de la pila que conducirá a la expectativa (al igual que en su captura de pantalla). Estoy de acuerdo en que podríamos imprimir la línea que arroja, pero a menudo la afirmación tiene varias líneas:

expect(a).toEqual({
  …
});

así que esto en realidad no se vería tan bien y tendríamos que usar un analizador para analizar el JS y extraer la información relevante (y colapsar las líneas largas) o algo similar para hacerlo bonito.

Personalmente, creo que estamos mostrando suficiente información por ahora, pero estamos felices de reconsiderarlo. Si tiene ideas para algo que no sea muy complejo pero agregue más contexto que ayude a resolver problemas más rápido, hágamelo saber.

de hecho, solíamos tener esto en Jasmine, pero descubrimos que en miles de archivos de prueba en FB, nadie lo usó

@cpojer por lo que el patrón es envolver cada aserción en un it ? y/o simplemente confiar en los números de línea?

¿Es posible que este patrón se haya adoptado menos porque es mejor o peor, y más simplemente por coherencia con las pruebas existentes? ¿O tal vez no saber que existe la función? No sabía que esto estaba en Jasmine.

Estoy de acuerdo en que podríamos imprimir la línea que arroja, pero a menudo la afirmación tiene varias líneas de largo.

¿La refactorización a una sola línea podría fomentar más información semántica en la afirmación? ¿Quizás?

const adminUser = {
  …
}
expect(a).toEqual(adminUser);

Personalmente, creo que estamos mostrando suficiente información por ahora, pero estoy feliz de reconsiderarlo.

El ejemplo anterior muestra que es difícil descubrir exactamente qué aserción falló a menos que agregue envoltorios detallados (IMO) alrededor de todo. Esto es especialmente cierto en un entorno transpilado donde los números de línea del mapa de origen no siempre son precisos. Creo que la comprensión rápida y precisa se rompió y dónde es importante, ya que son pruebas concisas.

Si tiene ideas para algo que no sea muy complejo pero agregue más contexto que ayude a resolver problemas más rápido, hágamelo saber.

Hice algunas sugerencias arriba:

¿Estás buscando algo más simple o diferente?

hola @timoxley! ya pensamos en agregar algo como esto.

entonces el problema con la primera opción es que algunos comparadores tienen argumentos opcionales, y eso complica más las cosas.

por ejemplo, aquí en el segundo caso, no sabremos si el argumento es una proximidad o un mensaje de error

expect(555).toBeCloseTo(111, 2, 'reason why');
expect(555).toBeCloseTo(111, 'reason why');

la segunda sugerencia no funcionará porque el comparador lanzará tan pronto como algo no cumpla con las expectativas

expect(1).toBe(2)/* will throw here */.because('reason');

podríamos adjuntar el motivo antes de que se ejecute el comparador, así:

expect(1).because('reason').toBe(2);
// or 
because('reason').expect(1).toBe(2);

pero esta API realmente no se ve tan bien.

otra opción sería agregar un segundo argumento a expect

expect(1, 'just because').toBe(2);

pero es más o menos lo mismo que la opción anterior.

La razón por la que creo que esto no es muy útil es porque los ingenieros no quieren perder el tiempo escribiendo pruebas. Cualquier cosa que hagamos para que sea más difícil para ellos solo conducirá a peores pruebas.

En el pasado, la mejor solución era crear emparejadores personalizados. Presentaremos expect.extend en la próxima versión de Jest y te permitirá crear fácilmente emparejadores como:

expect(a).toEqualMySpecificThing(…)

lo que debería permitirle escribir mensajes de falla más expresivos. He visto que esto se usa mucho en proyectos como Relay. Vea todos los emparejadores: https://github.com/facebook/relay/blob/master/src/tools/__mocks__/RelayTestUtils.js#L281

Cerrando por inactividad pero felices de reabrir si hay buenas ideas.

@cpojer @dmitriiabramov se disculpa por la demora.

Un segundo argumento para expect o encadenar un motivo con .because sería genial. ¿Qué hay que hacer para que esto suceda o no?

los ingenieros no quieren perder el tiempo escribiendo pruebas

@cpojer ¡De acuerdo! Además de no querer perder el tiempo depurando pruebas, esta es exactamente la razón por la que creo que sería preferible una API menos detallada con más contexto de fallas.

Para algunos números concretos, usando el ejemplo simple de mi comentario anterior, para obtener aserciones equivalentes + contexto con cinta, Jest requiere que el programador escriba casi el doble de la cantidad de repetitivo ceremonial:

  • 1.8x las líneas (6 vs 11)
  • 2x la sangría (1 vs 2)
  • 2x los parens/curlies (24 vs 48) !

¡Esto podría mejorarse!

// tape
test('api works', t => {
  t.deepEquals(api(), [], 'api without magic provides no items')
  t.deepEquals(api(0), [], 'api with zero magic also provides no items')
  t.deepEquals(api(true), [1,2,3], 'api with magic enabled provides all items')
  t.end()
})

// jest
describe('api works', () => {
  test('api without magic provides no items', () => {
    expect(api()).toEqual([])
  })
  test('api with zero magic also provides no items', () => {
    expect(api(0)).toEqual([])
  })
  test('api with magic enabled provides all items', () => {
    expect(api(true)).toEqual([1,2,3])
  })
})

Actualización : supongo que podría escribir las pruebas de broma en una sola línea con flechas:

// jest
describe('api works', () => {
  test('api without magic provides no items', () => expect(api()).toEqual([]))
  test('api with zero magic also provides no items', () => expect(api(0)).toEqual([]))
  test('api with magic enabled provides all items', () => expect(api(true)).toEqual([1,2,3]))
})

Esto genera algunas líneas más largas, pero mejora un poco las estadísticas que comparamos anteriormente:

  • 0.8x las líneas (6 vs 5)
  • 1x la sangría (1 vs 1)
  • 1.75x los parens/curlies (24 vs 42)

Sin embargo, creo que tener la descripción de la prueba al comienzo de la línea, sin un salto de línea, hace que sea más difícil analizar visualmente la lógica porque pone la "carne" de la prueba, es decir, las afirmaciones reales, en alguna posición de columna arbitraria.

Analizar el código es más importante que leer la descripción de la prueba, que es básicamente un comentario glorificado. Es por eso que nadie escribe comentarios al comienzo de la línea. Por ejemplo, esto sería una locura sadomasoquista:

/* api without magic provides no items */ expect(api()).toEqual([])
/* api with zero magic also provides no items */ expect(api(0)).toEqual([])
/* api with magic enabled provides all items */ expect(api(true)).toEqual([1,2,3])

Idealmente, todo el código de aserción se alinearía perfectamente en la misma columna para que un humano lo analice fácilmente. Basado en este pensamiento, optaría fuertemente por la forma final .because en lugar de la sugerencia alternativa de un segundo argumento para expect .

Gracias por mantener la conversación. Tenga en cuenta que tampoco necesita el bloque de descripción, lo que hace que las cosas sean más pequeñas. Desafortunadamente .because no funcionará porque cuando el comparador lanza (lo que sucede antes de que se llame a .because ), no tendremos forma de extraer el nombre.

¿Usar el último argumento de cada función de comparación entonces?

Solo funcionaría si lo agregamos como un segundo argumento de expect .
No podemos agregarlo como el último argumento para cada función de comparación debido a la ambigüedad.
p.ej

expect(obj).toHaveProperty('a.b.c', 'is that a reason or a value of the property?');

Cerrando por inactividad pero felices de reabrir si hay buenas ideas.

@cpojer no está claro si se ha descartado la forma jazmín (y otras) de expect(value).toBe(something, 'because message') .

Un ejemplo es probar cosas redux-saga/redux-observable donde está probando una máquina de estado . Es muy útil tener un mensaje descriptivo sobre en qué estado falló. Sin embargo, ese ejemplo es artificial, por lo que las descripciones también lo son.

@jayphelps ya no usamos la forma de jazmín desde que reescribimos todos los emparejadores de jazmín

@dmitriiabramov lo siento, mi pregunta no fue clara. ¿Se ha descartado que se vuelva a agregar la _manera_ de jazmín_ de hacerlo? Haciendo lo mismo que permiten.

@jayphelps como dije antes, no funcionará para todos los emparejadores debido a la ambigüedad.

expect(obj).toHaveProperty('a.b.c', 'is that a reason or a value of the property?');

y sing jest matchers se pueden ampliar con paquetes de terceros. No creo que sea una buena idea meterse con la lista de argumentos.

la opción más limpia es probablemente tenerlo como segundo argumento de expect , ya que siempre toma exactamente un argumento.

expect(123, 'jest because').toEqual(123);

Sin embargo, no estoy seguro de si queremos sobrecargar la API. Casi nunca lo usamos en las suites de prueba de Facebook y, para casos especiales, creo que es más fácil definir una nueva prueba:

beforeEach(someSharedSetup);
test('reason or description', () => expect(1).toBe(1));

son solo unas lineas mas :)

O incluso puede ponerlo en otra llamada describe() .

@dmitriiabramov Los casos molestos son cuando acumulas estado, como en una máquina de estado para sagas, epopeyas, etc. Cada prueba requiere los cambios de estado anteriores, aislarlos requiere una tonelada de duplicación sin ninguna ganancia AFAIK.

it('stuff', () => {
  const generator = incrementAsync();

  expect(generator.next().value).toBe(
    call(delay, 1000)
  );

  expect(generator.next().value).toBe(
    put({ type: 'INCREMENT' })
  );

  expect(generator.next()).toBe(
    { done: true, value: undefined }
  );
});

O incluso puede ponerlo en otra llamada describe().

¿Puedes elaborar esto? Las llamadas de descripción anidadas AFAIK fueron solo para dividir los títulos de las secciones, las pruebas aún se ejecutan simultáneamente, ¿verdad?

Los conjuntos de pruebas (archivos) se ejecutan simultáneamente, las llamadas test() no.

Estoy empezando a pensar que algo como

test('111' () => {
  jest.debug('write something only if it fails');
  expect(1).toBe(2);
});

puede ser una cosa

A partir de esta discusión y este repositorio , creo que uno agradable y semántico sería:

it('has all the methods', () => {
  since('cookie is a method').expect(reply.cookie).toBeDefined();
  since('download is a method').expect(reply.download).toBeDefined();
  since('end is a method').expect(reply.end).toBeDefined();
  // ...
});

El uso es similar al de because , pero tiene más sentido semánticamente.

Si le gusta esto, podría elaborar un PR agregando la funcionalidad since .

Por favor implemente una manera fácil de hacer esto. No lo uso muy a menudo, pero especialmente para pruebas más complicadas, es útil saber exactamente qué está fallando sin tener que buscar.

No diga "reescriba sus suites de prueba para que sean más simples". Lo único que los ingenieros odian más que _escribir_ conjuntos de pruebas es _reescribir_ conjuntos de pruebas.

Otra propuesta que he visto en alguna parte, no recuerdo dónde estaba en este mismo tema, con una explicación de por qué no funcionaría. Probablemente debería dormir un poco :)

Obtuve una demostración simple de "prototipo" funcionando, necesitaría implementar la recursividad ahora. Es un envoltorio delgado que usa Proxies alrededor de las variables globales y luego sobre cada método. Sin embargo, los proxies no son compatibles con los navegadores más antiguos y no se pueden polillenar, por lo que es posible que no sean aceptables para Jest. Esta es la estructura general del contenedor:

const since = (text) => {
  return new Proxy(global, {
    get: (orig, key) => {
      return (...args) => {
        try {
          const stack = orig[key](...args);
          return new Proxy(stack, {
            get: (orig, key) => {
              return (...args) => {
                try {
                  const ret = orig[key](...args);

                  // ... implement recursion here

                } catch (err) {
                  console.log('2', key, text, err);
                  throw err;
                }
              }
            }
          });
        } catch (err) {
          console.log('1', key, text, err);
          throw err;
        }
      };
    }
  });
};

Hay tres opciones realistas:

  • Esta forma es aceptable, por lo que debe agregarse a la biblioteca principal de Jest. Lo limpio y creo un PR.
  • Profundice en Jest y modifique la biblioteca principal. Mucho trabajo, por lo que no haría nada hasta que algunos miembros dijeran algo semioficialmente sobre este tema/dirección.
  • Termínelo de esta manera y publíquelo como un paquete. Indeseable ya que no es fácil de descubrir.

Editar: verlo en acción:

describe('Test', () => {
  it('works', () => {
    since('It fails!').expect('a').toEqual('b');
  });
});

Necesita un contexto de expectativas para que los resultados de las pruebas sean sensatos cuando tiene pruebas no triviales. Las pruebas del mundo real no siempre serían tan simples.

Recuerde los emparejadores personalizados: ocultan la complejidad matemática. Pero cuando la prueba falla, ocultar esta complejidad no es lo que desea porque desea obtener la máxima información sobre la falla. El contexto de expectativa le permite proporcionar este contexto manualmente. Supongo que no es lo ideal, sería mejor algún tipo de contexto automático, pero es la única forma que he visto hasta ahora.

Cuando rompo algo y falla, con Jest tengo que depurarlo manualmente o agregar registros o cualquier _modificación_, lo cual es mucho menos conveniente que simplemente mirar los resultados de la ejecución de la prueba.
En Jasmine, por ejemplo, tenemos la capacidad de imprimir algo de contexto para dar más sentido al fracaso.
En el marco de prueba más popular de Java, JUnit, también tenemos exactamente la misma función.

Lo siento si me equivoco, pero no veo ningún contraargumento _tecnológico_ para esta característica aquí. Y cosas como "esto no debería agregarse porque no se verá bien" son simplemente ridículas.

¿Podemos reabrir? Incluso jest.debug() como lo sugiere @aaronabramov arriba sería útil para mí.

This:

it('has all the methods', () => {
    since('cookie is a method', () => expect(reply.cookie).toBeDefined());
});

can be supported by adding this:


// setupTestFrameworkScriptFile.js
// http://facebook.github.io/jest/docs/configuration.html#setuptestframeworkscriptfile-string
global.since = (explanation, fn) => {
    try {
        fn();
    } catch(e) {
        e.message = explanation + '\n' + e.message;
        throw e;
    }
};

Además, jasmine-custom-message se parece a lo que se solicita:

describe('test', function() {
  it('should be ok', function() {
    since(function() {
      return {'tiger': 'kitty'};
    }).
    expect(3).toEqual(4); // => '{"tiger":"kitty"}'
  });
});

¿Hay planes para reabrir esto? Parece que ha habido duplicados de este problema recientemente. También estoy buscando mostrar un mensaje personalizado cuando falla la prueba.

Hmm... Esto también es algo que tengo en mi lista de deseos. Después de leer este hilo, puedo entender la respuesta relacionada con el uso de Jest en Facebook y no querer afectar su propio flujo de trabajo, pero ha habido algunas sugerencias que no interferirían con las pruebas existentes y agregarían la funcionalidad que a otros les gustaría. tener (incluido yo mismo).

¿Qué se necesitaría para que el formato 2nd arg to expect() o since() sea aceptado como PR? Estoy dispuesto a donar algo de tiempo para ayudar con esto.

Acabo de leer el hilo y veo buenos argumentos en ambos lados. Definitivamente quiero un mecanismo para proporcionar un mensaje de error personalizado por la misma razón que @timoxley publicó originalmente. Ahora mismo estoy haciendo algo como esto:

import assert from 'assert'
import chalk from 'chalk'

test('api works', () => {
  assert.deepEqual(
    api(),
    [],
    chalk.red('api without magic provides no items')
  )
  assert.deepEqual(
    api(0),
    [],
    chalk.red('api with zero magic also provides no items')
  )
  assert.deepEqual(
    api(true),
    [1, 2, 3],
    chalk.red('api with magic enabled provides all items')
  )
})

Esto realmente funciona muy bien, pero me gustaría evitar tener que usar tiza para obtener el color rojo (de lo contrario, se imprime sin color) y preferiría que esto sea compatible con expect .

Realmente no me importa cómo se implementa honestamente. Pero aquí hay una alternativa a since solo para lanzar algo más en caso de que otros lo prefieran:

const expectWithMessage = expect.withMessage(
  'api with magic enabled provides all items'
)
expectWithMessage(api(true)).toEqual([1, 2, 3])

// could be rewritten like
expect
  .withMessage('api with magic enabled provides all items')(api(true))
  .toEqual([1, 2, 3])

No estoy seguro de que me guste más que since . Soy bueno con lo que sea, realmente me encantaría tener esto :)

Ah, y para abordar el comentario:

La razón por la que creo que esto no es muy útil es porque los ingenieros no quieren perder el tiempo escribiendo pruebas. Cualquier cosa que hagamos para que sea más difícil para ellos solo conducirá a peores pruebas.

Estoy de acuerdo en que no queremos que sea más difícil escribir pruebas. Es por eso que esto sería un cambio aditivo. Entonces, las personas que no quieren "perder el tiempo" haciendo que sus pruebas sean más fáciles de depurar, pueden omitir los mensajes útiles, pero luego ingresarán a una base de código que tiene mensajes útiles como este y luego agradecerán el ingeniero que se tomó el tiempo de explicar un poco la afirmación :wink:

Hola , @cpojer, ¿ alguna actualización sobre esto?

Jest está funcionando muy bien para mí, excepto por este problema ... En este momento, estoy luchando para corregir una afirmación fallida dentro de un ciclo for como
expectationsArray.forEach(expectation => expect(...))

Es difícil averiguar exactamente qué expectativas fallan sin un mensaje de error personalizado (a menos que lo esté haciendo mal...?)

Gracias

@mj-airwallex, es bueno envolver las expectativas con un test en un bucle for, por ejemplo:

const expectationsArray = [[0, 'a'], [1, 'b']];

expectationsArray.forEach(([expectation, desc]) => {
  test(`test ${desc}`, () => {
    expect(expectation).toBeGreaterThanOrEqual(2);
  });
});

También tengo un problema con la broma debido a la necesidad de proporcionar mensajes personalizados durante las expectativas. Envolver las expectativas por debajo test parece funcionar para los casos en los que no hay necesidad de llamadas asincrónicas. Pero como Jest no puede manejar describe (#2235) devolviendo una promesa, no podemos crear pruebas con llamadas asincrónicas junto con envolverlas con test . Y no podemos tener múltiples test anidados.

Aquí hay un ejemplo para ilustrar el problema:

async function getArray() {
  return [0,0,0,0,0,0]
}

describe('Custom messages with async', async () => {
  const array = await getArray();
  array.forEach((item) => {
    test(`test${item}`, () => {
      expect(item).toBe(0)
    });
  });
})

¿Alguna idea de cómo manejar esto?

Mirando el problema en el OP ("Sería bueno si hubiera algún contexto legible por humanos que dejara claro de inmediato qué expectativa falló"), creo que ahora está resuelto. A partir de Jest 22 imprimimos el contexto de la afirmación fallida. ¿Sigue siendo necesario el mensaje adicional? Si _es_, puede ser un comentario de código arriba o al lado de la afirmación

image

La descripción asíncrona es otro problema (que no será ayudado por el marco de código agregado)

Creo que no usaría descripción asíncrona y en su lugar usaría beforeEach o beforeAll

@kentcdodds , ¿podría dar un ejemplo de cómo manejar esto con beforeEach o beforeAll ? Si intenta generar todos los resultados de llamadas asincrónicas necesarios en beforeEach y beforeAll , al final lo obligará a crear test anidados que no están permitidos.

Ah, me perdí lo que estabas haciendo con esa llamada asíncrona. Lo siento 😅 Sí, no podrías hacer beforeEach o beforeAll para hacer eso.

@SimenB , imprimir el contexto ya ayuda mucho y resuelve la mayoría de los problemas con mensajes personalizados. ¡Gracias por esto! Pero sería bueno tener la posibilidad de mensajes personalizados explícitamente como un argumento, ya que ayuda en situaciones como el uso de expectativas dentro de los bucles.

Mirando el problema en el OP ("Sería bueno si hubiera algún contexto legible por humanos que dejara claro de inmediato qué expectativa falló"), creo que ahora está resuelto.

Sí, esto resuelve el problema original, siempre que tenga acceso a la fuente original antes de la transpilación, que tendrán la mayoría de los usuarios de Jest. En mi opinión, es un poco torpe en comparación con permitir que los usuarios simplemente impriman un mensaje proporcionado por el usuario con la falla, pero supongo que es suficiente.

Acabo de empezar a usar Jest y me falta una función como esta. Mi caso de uso:
Una prueba está fallando cuando afirmo que una propiedad de un objeto es verdadera. Me ayudaría a comprender la falla más rápido si pudiera registrar el objeto en caso de que falle la afirmación.

Puedes usar toHaveProperty para eso.

test('property', () => {
  expect({foo: 'bar'}).toHaveProperty('baz', 'foobar');
});

image

Si solo desea verificar que está allí, elimine el segundo argumento. Si solo desea afirmar que tiene _algún_ valor, puede usar expect.anything() .
toMatchObject es otra alternativa.

También puede usar assert si lo desea.

test('property', () => {
  const obj = {foo: 'bar'};
  assert.equal(obj.baz, 'foobar', JSON.stringify(obj));
});

image

Gracias por el consejo. assert.equal(obj.baz, 'foobar', JSON.stringify(obj)); haría el trabajo en mi caso particular.

@SimenB @mpseidel ¿qué es afirmar? ¿Es una biblioteca de terceros? No puedo encontrar nada en jest docs.

@sharikovvladislav assert es un módulo central de nodo https://nodejs.org/api/assert.html

@mpseidel ups ! no lo sabía Gracias. Funciona.

Estoy usando el siguiente fragmento de código para evitar esta limitación del marco (en TypeScript pero simplemente elimine las anotaciones de tipo para JS)
export const explain = (expectation: () => void, explanation: string) => { try { expectation(); } catch(e) { console.log(explanation) throw e; } }

Hola,
Me sorprende que nadie haya mencionado los bucles todavía. El mensaje no sería solo una cadena, sino una cadena dinámica según la iteración del ciclo.
jest-plugin-context es bueno, gracias por esto funciona, pero es un poco pesado y el problema inicial sigue siendo relevante en mi opinión.
mira esta prueba

describe('MyStuff', () => {
    it('should render and contain relevant inputs', () => {
      const wrapper = shallowWrapped(<MyStuff />);
      const expectedKeys = ['love','jest','but','need','details','for','expect'];
      expectedKeys.forEach((key) => {
        expect(wrapper.find({ id: key }).length).toEqual(1);
      });
    });
  });

Buena suerte encontrando a tu culpable. En este momento debo agregar una línea o probar un objeto como {len:.., key:..} , esto no es limpio y no es fácil de usar.
Creo que este caso de uso es relevante, por ejemplo, para formularios y verificación de representación de elementos.
La sintaxis podría ser tan simple como toEqual(1).context("my message") o toEqual(1, "my message") (aunque, por supuesto, sé que la implementación siempre es más difícil y respeto el gran trabajo que hiciste con Jest).

Tal vez use el mismo formato que chai , es decir, agregue el mensaje como un segundo argumento a la llamada expect:

expect(foo, 'this detail').toEqual(2)

T

Usé jazmín antes, así que vine aquí para descubrir que no es compatible.
Sin embargo, estas cosas son todas funciones. ¿No podemos simplemente hacer algo como:

describe('MyStuff', () => {
    describe('should render and contain relevant inputs', () => {
      const wrapper = shallowWrapped(<MyStuff />);
      const expectedKeys = ['love','jest','but','need','details','for','expect'];

      expectedKeys.forEach((key) => {
        it(`contains key "${key}"`, () =>
          expect(wrapper.find({ id: key }).length).toEqual(1)
        )
      })
  });
});

2018-04-18-222246_646x390_scrot

@akkerman Buena solución. Dado que describe y it son globales mágicos proporcionados por broma, debo admitir que pueden sentirse oscuros, no estaba seguro de que escribir 't' en un bucle pudiera funcionar.

¿Qué hay de encadenar otro modificador?

expect(foo).toEqual(bar).because('reason with %s placeholders')

O tal vez una función

expect(foo).toEqual(bar).explainedBy((result) => `Lorem ipsum ${result}`)

Creo que otro modificador rápidamente se vuelve ilegible.
T

2018-04-19 13:47 GMT+02:00 λ • Geovani de Souza [email protected] :

¿Qué hay de encadenar otro modificador?

expect(foo).toEqual(bar).because('motivo con %s marcadores de posición')

O tal vez una función

expect(foo).toEqual(bar).explainedBy((resultado) => Lorem ipsum ${result} )


Estás recibiendo esto porque comentaste.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/facebook/jest/issues/1965#issuecomment-382705387 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AAM5PwBCvET1KdEDeDEF7gGo708Naj8oks5tqHlSgaJpZM4Kc6Uu
.

--


Tarjei Huse
Móvil: 920 63 413

La forma en que funciona expect es lanzando, por lo que no funcionaría de todos modos.

Creo que expect(something, 'some helpful text on failure').toEqual(somethingElse) o expect.context(something, 'some helpful text on).toEqual(somethingElse) son las mejores alternativas, pero no me gusta ninguna de ellas.

¿Se puede reabrir esto? Parece que Jest todavía no tiene una buena solución para probar cómo cambia el estado en múltiples interacciones, por ejemplo:

  • probar cómo cambia un contenedor React con estado en respuesta a una serie de eventos
  • probar cómo cambia una página web a lo largo de múltiples interacciones usando Puppeteer

Ambos casos requieren realizar una serie de acciones y afirmar cómo cambió (o no cambió) el estado después de cada acción, por lo que a veces son necesarias pruebas de afirmación múltiple. No siempre es posible resolver este tipo de problema con beforeEach .

Sigo encontrando situaciones en las que esto sería realmente útil. Específicamente cuando ejecuto múltiples interacciones y afirmaciones como explica @callumlocke .

Si creamos una API que la gente no odie, ¿es algo que estaría dispuesto a seguir? Realmente creo que esta sería una característica valiosa y muy utilizada.

He aquí un resumen de las soluciones propuestas:

expect(api()).toEqual([]) // api without magic provides no items
it('api without magic provides no items', () => expect(api()).toEqual([]))
test('api without magic provides no items', () => expect(api()).toEqual([]))
expect(api()).toHaveNoItems()

expect(api(), 'api without magic provides no items').toEqual([])
expect(api()).because('api without magic provides no items').toEqual([])
since('api without magic provides no items').expect(api()).toEqual([]))
because('api without magic provides no items').expect(api()).toEqual([]))
jest.debug('api without magic provides no items'); expect(api()).toEqual([]))

Tenga en cuenta que un .because() final no es posible, por lo que no se incluye como opción.

Las cuatro opciones del primer grupo son compatibles hoy. Personalmente, encuentro que la primera opción (un marco de código con un comentario) funciona muy bien. E incluso mejor que eso es usar un comparador personalizado (opción 4).

Creo que lo que debemos entender para avanzar en esto es: ¿qué es más atractivo de las opciones del segundo grupo que las opciones del primero? ¿Qué agrega el segundo grupo que puede justificar el mantenimiento central de todos los comparadores que proporcionamos (a través de comparadores asíncronos, comparadores asimétricos, comparadores de espías, comparadores de lanzamiento, emparejadores de promesa y emparejadores personalizados)?

Hola,
Básicamente tienes algunos casos de uso:

  • Pruebas de afirmaciones múltiples (necesita afirmar varias cosas en una prueba)
  • Contexto de aserción generado dinámicamente (quiere una variable en su mensaje de falla para que quede más claro, por ejemplo, para imprimir un campo específico de su objeto fallido porque recibió muchas pruebas)
  • Aserciones generadas dinámicamente (haces un bucle que genera aserciones)

Las opciones del primer grupo están pensadas principalmente para el primer caso de uso. Si encuentra la necesidad de una aserción generada dinámicamente, como se propone, puede anidar llamadas a it y test , de modo que una prueba pueda generar nuevas pruebas en un bucle. El problema es que generas pruebas y no afirmaciones . Imagine que quiero afirmar algo en cada elemento de una matriz de 1000 elementos, esto inflará los resúmenes de prueba.

Sin embargo, dado que esos casos de uso dinámico aún son raros, en mi opinión, deberíamos apegarnos a la solución que requiere un trabajo mínimo para los mantenedores. Personalmente, me gusta la solución because/since , porque suena bastante simple. Supongo que la implementación sería principalmente envolver expect en un try/catch que imprime el mensaje y lo devuelve.
jest.debug suena extraño, para mí, la depuración es imprimir un mensaje incluso si las pruebas realmente pasan
La opción "último argumento" también es buena, pero no estoy seguro de si es factible ya que expect puede aceptar una cantidad variable de argumentos.

Estoy bien con cualquier opción. Solo quiero la característica. Tampoco me gusta mucho la API jest.debug , pero si es la única que tiene sentido, estoy de acuerdo porque solo quiero esta función.

@kentcdodds ¿qué pasa con las cuatro opciones existentes?

@eric-burel, ¿has visto test.each y describe.each agregados en Jest 23 (disponible de forma independiente para Jest <23)?

Como dije, realmente no me importa con qué opción vayamos. Solo quiero que la función exista. Supongo que si tuviera que ordenarlos por orden de preferencia sería:

  1. expect(api(), 'api without magic provides no items').toEqual([])
  2. because('api without magic provides no items').expect(api()).toEqual([]))
  3. since('api without magic provides no items').expect(api()).toEqual([]))
  4. expect(api()).because('api without magic provides no items').toEqual([])
  5. jest.debug('api without magic provides no items'); expect(api()).toEqual([]))

(test|describe).each es excelente, pero no resuelve el problema en el que desea tener múltiples acciones/afirmaciones en una sola prueba.

La característica existe hoy con cuatro opciones:

expect(api()).toEqual([]) // api without magic provides no items
it('api without magic provides no items', () => expect(api()).toEqual([]))
test('api without magic provides no items', () => expect(api()).toEqual([]))
expect(api()).toHaveNoItems()

¿Qué hay de malo en estos? Las _nuevas_ soluciones propuestas solo parecen ser marginalmente mejores que las soluciones existentes. ¿Qué beneficios aportan sobre lo que tenemos que justifiquen el coste de mantenimiento?

@rickhanlonii Bueno, no sabía sobre test.each , esa es realmente una gran característica, gracias por señalar esto. Creo que resuelve el problema para mi tercer caso de uso, prueba generada dinámicamente desde una matriz.

Entonces dejó el segundo que enumeré: tener un mensaje de falla generado dinámicamente, lo que haría que la depuración fuera más rápida. No tengo muchos casos de uso en este momento, tal vez cuando prueba el valor de un campo de objeto, le gustaría imprimir todo el objeto en caso de falla. En mi opinión, es legítimo, como cualquier cosa que facilite la prueba de escritura, incluso si es marginal o un poco redundante. Después de todo, ambos podemos escribir it y test , a menos que haya una diferencia que no conozco, esto es principalmente por comodidad.
Es marginal pero realmente algo esperado (sin juego de palabras) por los usuarios, como muestra este hilo.

Editar: crear una prueba en otra prueba con it o test y un nombre generado dinámicamente para la prueba es una solución válida, pero realmente no me gusta crear una prueba cuando me refiero a crear una afirmación . Nunca habría adivinado que era posible si la solución no se hubiera dado en este hilo.

Esto es Loco. Simplemente agregue un segundo parámetro opcional a expect(). Aquellos de nosotros que queramos usarlo lo haremos (de manera selectiva), y aquellos que no, no lo haremos.

Mocha ha estado haciendo esto desde siempre... es una de las razones por las que abandoné a Jasmine hace años (la otra es mucho mejor burlándose del temporizador). Si no tuviera que unirme al carro de React, no estaría usando Jest ni ningún otro otro derivado del jazmín.

Imprimir un mensaje de error es una convención en muchos otros marcos de prueba y me sorprendió no verlo en Jest. Encontré muchos ejemplos útiles en este hilo (gracias por ellos), pero agregar una forma explícita de imprimir un error personalizado en caso de falla de prueba sería una buena adición a la facilidad de uso de Jest. Esto facilitaría a los desarrolladores que están acostumbrados a otros marcos de prueba (incluidos los que no son JS) aumentar Jest.

@mattphillips , ¿crees que es posible hacer algo similar a jest-chain aquí para permitir que exista una solución en el espacio del usuario? Por ejemplo, segundo argumento para expect

Honestamente, esto es algo muy estándar en la mayoría de los marcos de prueba de JS. Muy decepcionado de no encontrarlo en Jest mientras escribimos todas nuestras pruebas con un mensaje de error personalizado.

@SimenB , lo siento, ¡solo me di cuenta de tu mensaje esta mañana!

Sí, esto es factible en el espacio del usuario, lo acabo de eliminar y lo publiqué como jest-expect-message https://github.com/mattphillips/jest-expect-message

Comentarios bienvenidos :sonrisa:

Impresionante, gracias por hacerlo!

@cpojer

La razón por la que creo que esto no es muy útil es porque los ingenieros no quieren perder el tiempo escribiendo pruebas. Cualquier cosa que hagamos para que sea más difícil para ellos solo conducirá a peores pruebas.

Dos cosas:

  1. Agregar un segundo argumento opcional a expect() no hace que nada sea más difícil para los desarrolladores.
  2. Lo último que quieren hacer los desarrolladores es perder el tiempo depurando lo que causó que la prueba fallara. A menudo, esperar vs recibir es una buena manera de verificar si se cumplió una condición, pero a menudo no hay suficiente contexto sobre qué causó que fallara.

Usé Mocha/Chai, así como cinta, antes de llegar a Jest, y esto es realmente un factor decisivo. ¿Qué tenemos que hacer para que se espere un soporte de mensajes personalizado?

Decirnos que expect.extend para crear un comparador personalizado suena exactamente como lo que estaba tratando de evitar en su primer argumento: "los ingenieros no quieren perder el tiempo escribiendo pruebas".

Me resulta fácil abrir la prueba unitaria y mirar el número de línea, por lo que el caso de uso en el OP no me molesta.

El caso de uso que me molesta es cuando tengo un bucle dentro de una prueba, por ejemplo, para probar cada valor de una enumeración, por ejemplo, así:

it("Should contain at least one word of every wordType", () => {
  for (const wordType of wordTypes) {
    expect(words.find((word) => word.wordType === wordType)).toBeTruthy();
  }
});

Si eso falla, entonces no sé qué valor de tipo de palabra falló.

Mi solución alternativa fue reemplazar eso con un mensaje que contiene el resultado de la prueba, y esperar que el mensaje contenga el resultado de la prueba esperado (es decir, verdadero). Si falla, Jest imprime el mensaje que contiene la información adicional.

    expect(`${wordType} ${!!words.find((word) => word.wordType === wordType)}`).toEqual(`${wordType} ${true}`);

Jest imprime esto...

expect(received).toEqual(expected)

Difference:

- Expected
+ Received

- 6 true
+ 6 false

... lo que me dice que wordType era 6 cuando falló.

O más legiblemente algo así como...

    if (!words.find((word) => word.wordType === wordType)) {
      expect(`Didn't find a word with wordType '${wordType}'`).toEqual(null);
    }

@cwellsx revisa las pruebas parametrizadas con test.each esta manera cada tipo de palabra será una prueba independiente con un título descriptivo (basado en el valor)

Esto sería increíblemente útil en pruebas como esta:

test("compare ArrayBufferCursors", () => {
    const orig: ArrayBufferCursor;
    const test: ArrayBufferCursor;

    expect(test.size).toBe(orig.size);

    while (orig.bytes_left) {
        expect(test.u8()).toBe(orig.u8());
    }
});

En este momento, solo sé que algún byte en ArrayBufferCursor está mal, pero no tengo idea de cuál. Poder agregar un índice como contexto facilitaría mucho la depuración. Afortunadamente, algunas personas han presentado soluciones aquí, pero todas son feas y lentas.

@rickhanlonii , entiendo que quieras reducir los costos de mantenimiento de jest, pero las opciones que ofreces en tu comentario aumentan el mantenimiento de las pruebas unitarias de todos los demás proyectos. Si quiero explicar una afirmación usando toEqual , que por lo demás es perfectamente suficiente, ¿realmente me está sugiriendo que presente un comparador personalizado?

Si bien hay casos con pruebas repetitivas, donde it.each son útiles, tener que agregar un código innecesario para un comentario simple sobre una afirmación hace que este marco de prueba sea más difícil de usar.

Esta discusión y el surgimiento de jest-expect-message me recuerda el problema de beforeAll en Jasmine...

Puede que esté malinterpretando la solicitud del OP aquí, pero el problema que estaba tratando de resolver y me trajo a este hilo de problemas se resolvió simplemente usando un simple try / catch y un envoltorio alrededor jest.expect . Todo lo que quería hacer era registrar la totalidad de los objetos que esperaba frente a los que recibí + algunos registros básicos que explican el significado. Por supuesto, esto podría extenderse para hacer casi lo que quieras.

La versión más genérica de esto podría verse así:

in some test utility file...

const myExpect = (expectFn, errorCallback) => {
  try {
    expectFn();
  } catch (err) {
    errorCallback();
    throw new Error(err);
  }
};

// in the actual test suite...

const context = { hello: "world", results, expected };
myExpect(
    () => expect(results).toEqual(expected),
    () => console.error("[Error] -- context:", JSON.stringify(context))
);

+1
Hola, a mí también me encantaría esta característica. Me haría la vida mucho más fácil.

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