El autor del material, cuya primera parte de la traducción publicamos hoy, dice que él, como consultor independiente en Node.js, analiza más de 10 proyectos cada año. Sus clientes, lo cual está bastante justificado, le piden que preste especial atención a las pruebas. Hace unos meses, comenzó a tomar notas sobre técnicas de prueba valiosas y los errores que encontró. El resultado fue material que contenía tres docenas de recomendaciones para la prueba.

En particular, se enfocará en la selección de los tipos de prueba que son adecuados en una situación particular, en su diseño adecuado, en la evaluación de su efectividad y en dónde exactamente en las cadenas de CI / CD necesita colocarlos. Algunos de los ejemplos aquí se ilustran con Jest, algunos con Mocha. Este material se centra principalmente no en herramientas, sino en metodología de prueba.
→
Prueba de proyectos de Node.js. Parte 2. Evaluación del rendimiento de la prueba, integración continua y análisis de calidad del código.▍0. Regla de oro: las pruebas deben ser muy simples y directas
¿Conoces a alguien: un amigo, un miembro de la familia, el héroe de la película, a quien siempre se le acusa de buen humor y siempre está listo para ofrecer una mano amiga sin requerir nada a cambio? Así es como se deben diseñar buenas pruebas. Deben ser simples, beneficiosos y provocar emociones positivas. Esto se puede lograr mediante una cuidadosa selección de métodos de prueba, herramientas y objetivos. Aquellos cuyo uso justifica el tiempo y el esfuerzo dedicado a la preparación y realización de pruebas y al mismo tiempo da buenos resultados. Solo necesita probar lo que necesita ser probado, debe esforzarse por asegurarse de que las pruebas sean simples y flexibles, y a veces puede rechazar algunas pruebas, sacrificando significativamente la confiabilidad del proyecto por su simplicidad y velocidad de desarrollo.
Las pruebas no deben considerarse como código de aplicación normal. El hecho es que un equipo típico involucrado en el desarrollo de un proyecto, en cualquier caso, hace todo lo posible para mantenerlo en condiciones de trabajo, es decir, se esfuerza por, por ejemplo, que un producto comercial funcione como sus usuarios esperan. Como resultado, tal equipo puede no tener una muy buena sensación de que tendrá que apoyar otro "proyecto" complejo representado por un conjunto de pruebas. Si las pruebas del código principal crecen, atraen cada vez más atención hacia sí mismas y se convierten en una causa de preocupación constante, entonces el trabajo en ellas será abandonado o, al tratar de mantener un nivel decente, les dará tanto tiempo y energía que esto retrasará el trabajo en el proyecto principal.
Por lo tanto, el código de prueba debe ser lo más simple posible, con un número mínimo de dependencias y niveles de abstracción. Las pruebas deben verse para que puedan entenderse de un vistazo. La mayoría de las recomendaciones que consideraremos aquí surgen de este principio.
Sección 1. Anatomía de las pruebas.
▍1. Diseñe sus pruebas para que el informe le diga qué se está probando, en qué escenario y qué se espera de las pruebas.
Recomendaciones
El informe de prueba debe indicar si la versión actual de la aplicación cumple con sus requisitos. Esto debe hacerse de una manera que sea comprensible para aquellos que no tienen que estar familiarizados con el código de la aplicación. Puede tratarse de un probador, un especialista de DevOps que está implementando el proyecto, o el desarrollador mismo, que examinó el proyecto algún tiempo después de escribir su código. Esto se puede lograr si, al escribir pruebas, se enfoca en los requisitos del producto. Con este enfoque, la estructura de la prueba se puede imaginar que consta de tres partes:
- ¿Qué se está probando exactamente? Por ejemplo, el método
ProductsService.addNewProduct
. - ¿En qué escenario y bajo qué circunstancias se realiza la prueba? Por ejemplo, la reacción del sistema se verifica en una situación en la que el precio de los bienes no se ha pasado al método.
- ¿Cuáles son los resultados esperados de la prueba? Por ejemplo, en una situación similar, el sistema se niega a confirmar la adición de un nuevo producto.
Consecuencias de la desviación de las recomendaciones
Suponga que el sistema no se puede implementar, y del informe de prueba solo puede descubrir que no pasó la prueba llamada
Add product
, que verifica la adición de un determinado producto. ¿Esto dará información sobre qué salió mal exactamente?
Enfoque correcto
La información de prueba consta de tres piezas de información.
Enfoque correcto
El informe de prueba se asemeja a un documento que contiene una declaración de los requisitos del producto.
Así es como se ve a diferentes niveles.
Documento de requisitos del producto, nombre de prueba, resultados de prueba- Un documento con requisitos de producto puede ser, de hecho, un documento especial, o puede existir en forma de correo electrónico.
- Al nombrar las pruebas, que describen el propósito de las pruebas, su escenario y los resultados esperados, debe cumplir con el lenguaje que se utiliza para formular los requisitos del producto. Esto lo ayudará a comparar el código de prueba y los requisitos del producto.
- Los resultados de la prueba deben ser claros incluso para aquellos que no están familiarizados con el código de la aplicación o lo han olvidado por completo. Estos son probadores, especialistas de DevOps, desarrolladores que vuelven a trabajar con el código unos meses después de escribirlo.
▍2. Describa lo que espera de las pruebas en el lenguaje del producto: use declaraciones estilo BDD
Recomendaciones
El desarrollo de pruebas en un estilo declarativo permite a quienes trabajan con ellas captar instantáneamente su esencia. Si las pruebas se escriben utilizando un enfoque imperativo, están llenas de construcciones condicionales que complican enormemente su comprensión. Siguiendo este principio, las expectativas deben describirse en un lenguaje cercano al ordinario. El estilo declarativo de BDD utiliza las construcciones
expect
o
should
, en lugar de algún código especial de su propio diseño. Si no hay declaraciones necesarias en Chai o Jest, y resulta que tales declaraciones a menudo son necesarias, considere
agregar nuevos "cheques" a Jest o escribir sus propios
complementos para Chai .
Consecuencias de la desviación de las recomendaciones
Si no sigue las recomendaciones descritas anteriormente, terminará con el hecho de que los miembros del equipo de desarrollo escribirán menos pruebas y omitirán las molestas comprobaciones utilizando el método
.skip()
.
Enfoque equivocado
El lector de esta prueba tendrá que revisar completamente el código imperativo bastante largo solo para comprender qué se prueba exactamente en la prueba.
it("When asking for an admin, ensure only ordered admins in results" , ()={
Enfoque correcto
Literalmente puedes entender esta prueba de un vistazo.
it("When asking for an admin, ensure only ordered admins in results" , ()={
▍3. Realizar código de prueba linting utilizando complementos especiales
Recomendaciones
Existe un conjunto de complementos para ESLint diseñados específicamente para analizar el código de prueba y para encontrar problemas en dicho código. Por ejemplo, el
complemento eslint-plugin-mocha dará advertencias si la prueba se escribe a nivel global (y no es un descendiente de
describe()
), o si las pruebas se
omiten , lo que puede dar falsas esperanzas de que todas las pruebas se superen. El
complemento eslint-plugin-jest funciona de manera similar, por ejemplo, advirtiendo sobre pruebas que no tienen declaraciones, es decir, sobre aquellas que no verifican nada.
Consecuencias de la desviación de las recomendaciones
El desarrollador estará feliz de ver que el código está cubierto en un 90% en las pruebas y el 100% de las pruebas se pasan con éxito. Sin embargo, permanecerá en este estado solo hasta que resulte que muchas pruebas, de hecho, no verifican nada, y algunos scripts de prueba simplemente se omiten. Uno solo puede esperar que nadie implemente proyectos que son "probados" de esta manera en la producción.
Enfoque equivocado
El escenario de prueba está lleno de errores que, afortunadamente, se pueden detectar con el linter.
describe("Too short description", () => { const userToken = userService.getDefaultToken()
▍4. Apéguese al método de la caja negra: pruebe solo métodos públicos
Recomendaciones
Probar algunos mecanismos de código interno significa un aumento significativo en la carga de los desarrolladores y casi no brinda beneficios. Si una determinada API produce los resultados correctos, ¿vale la pena pasar varias horas probando sus mecanismos internos y luego seguir apoyando estas pruebas, que se rompen fácilmente, actualizadas? Al probar métodos disponibles públicamente, también se verifica su implementación interna, aunque implícitamente. Dicha prueba dará un error si surge un problema en el sistema, lo que resulta en la emisión de datos incorrectos. Este enfoque también se llama "pruebas de comportamiento". Por otro lado, al probar los mecanismos internos de una determinada API (es decir, utilizando la técnica de "recuadro blanco"), el desarrollador se centra en los pequeños detalles de la implementación y no en el resultado final del código. Las pruebas que verifican tales sutilezas pueden comenzar a generar errores, por ejemplo, después de una pequeña refactorización del código, a pesar de que el sistema continúa produciendo resultados correctos. Como resultado, este enfoque aumenta significativamente la carga en el programador asociado con el soporte del código de prueba.
Consecuencias de la desviación de las recomendaciones
Las pruebas que intentan capturar los mecanismos internos de cierto sistema se comportan como un pastorcillo de una
fábula que llamó a los campesinos con gritos de "¡Ayuda! ¡Lobo! ”Cuando no había ningún lobo cerca. La gente corrió a la ayuda solo para descubrir que habían sido engañados. Y cuando el lobo realmente apareció, nadie vino al rescate. Tales pruebas producen resultados falsos positivos, por ejemplo, en aquellos casos en que cambian los nombres de algunas variables internas. Como resultado, no es sorprendente que el que realiza estas pruebas pronto comience a ignorar sus "gritos", lo que, en última instancia, lleva al hecho de que una vez que un error grave puede pasar desapercibido.
Enfoque equivocado
Esta prueba prueba los mecanismos internos de una clase sin razón particular para tales comprobaciones.
class ProductService{
▍5. Elija los objetos de respaldo apropiados: evite las turbas, prefiera trozos y espías
Recomendaciones
El uso de la prueba se duplica cuando la prueba es un mal necesario, ya que están asociados con los mecanismos internos de la aplicación. Sin algunos de ellos, es imposible hacerlo.
Aquí hay material útil sobre este tema. Sin embargo, varios enfoques para el uso de tales objetos no pueden llamarse equivalentes. Por lo tanto, algunos de ellos, talones (stub) y espías (espía), tienen como objetivo probar los requisitos del producto, pero, como un efecto secundario inevitable, se ven obligados a afectar ligeramente los mecanismos internos de este producto. Los simulacros, por otro lado, están destinados a probar los mecanismos internos del proyecto. Por lo tanto, su uso conduce a una enorme carga innecesaria en los programadores, de lo que hablamos anteriormente, ofreciendo adherirse a la metodología de "caja negra" al escribir pruebas.
Antes de usar objetos duplicados, hágase una pregunta simple: "¿Los uso para probar la funcionalidad que se describe, o podría describirse en los requisitos técnicos para el proyecto?". Si la respuesta a esta pregunta es negativa, esto puede significar que va a probar el producto utilizando el enfoque de "caja blanca", del cual ya hemos hablado sobre las deficiencias.
Por ejemplo, si desea averiguar si su aplicación funciona correctamente en una situación en la que un servicio de pago no está disponible, puede detener este servicio y hacer que la aplicación reciba algo que indique que no hay respuesta. Esto le permitirá verificar la reacción del sistema ante una situación similar, para averiguar si se comporta correctamente. En el curso de dicha prueba, se verifica el comportamiento, la respuesta o el resultado de la aplicación en ciertas condiciones. En esta situación, puede usar el espía para verificar si, al detectar una caída en el servicio de pago, se envió un determinado correo electrónico. Esto, nuevamente, será una verificación del comportamiento del sistema en una situación determinada, que, con seguridad, se registra en los requisitos técnicos para ello, por ejemplo, en el siguiente formulario: "Enviar un correo electrónico al administrador si el pago no se aprueba". Por otro lado, si usa un objeto simulado para representar un servicio de pago y verifica la operación cuando accede a él con la transferencia de lo que espera de él, hablaremos sobre probar mecanismos internos que no están directamente relacionados con la funcionalidad de la aplicación y, bastante Quizás puede cambiar a menudo.
Consecuencias de la desviación de las recomendaciones
Con cualquier refactorización de código, deberá buscar todos los moki, refactorización y su código. Como resultado, el soporte de prueba se convertirá en una carga pesada, haciéndolos enemigos del desarrollador, no sus amigos.
Enfoque equivocado
Este ejemplo muestra un objeto simulado centrado en probar los mecanismos internos de la aplicación.
it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => {
Enfoque correcto
Los espías están destinados a probar los sistemas para cumplir con sus requisitos, pero, como efecto secundario, inevitablemente afectan los mecanismos internos de los sistemas.
it("When a valid product is about to be deleted, ensure an email is sent", async () => {
▍6. Durante las pruebas, use información realista, no limitada a algo como "foo"
Recomendaciones
A menudo, los errores en la producción se manifiestan en una combinación de circunstancias muy específica e incluso sorprendente. Y esto significa que cuanto más cerca de la realidad estén los datos de entrada utilizados durante las pruebas, mayor será la probabilidad de detección temprana de errores. Se usa para generar datos que se asemejan a bibliotecas especializadas reales como
Faker . Por ejemplo, tales bibliotecas generan números de teléfono aleatorios pero realistas, nombres de usuario, números de tarjetas bancarias, nombres de compañías e incluso textos de "lorem ipsum". Además, considere el uso de datos de entornos de producción en las pruebas. Si desea llevar tales pruebas a un nivel aún mayor, consulte nuestra próxima recomendación sobre pruebas basadas en la propiedad.
Consecuencias de la desviación de las recomendaciones
Cuando se prueba un proyecto durante su desarrollo, todas las pruebas se pueden pasar solo si se llevan a cabo utilizando datos poco realistas como las líneas "foo". Pero en producción, el sistema fallará en una situación en la que el pirata informático le dé algo así como
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
.
Enfoque equivocado
El sistema pasa con éxito estas pruebas solo porque usan datos poco realistas.
const addProduct = (name, price) =>{ const productNameRegexNoSpace = /^\S*$/;
Enfoque correcto
Utiliza datos aleatorios similares a los datos reales.
it("Better: When adding new valid product, get successful confirmation", async () => { const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
▍ 7. Sistemas de prueba que usan múltiples combinaciones de entrada usando pruebas basadas en propiedades
Recomendaciones
Por lo general, las pruebas usan pequeños conjuntos de datos de entrada. Incluso si se parecen a datos reales (hablamos de esto en la sección anterior), tales pruebas cubren solo un número muy limitado de posibles combinaciones de entradas de la entidad investigada. Por ejemplo, podría verse así:
(method('', true, 1), method("string" , false" , 0))
. El problema es que en la API de producción, que se llama con cinco parámetros, puede obtener la entrada de miles de diferentes variantes de sus combinaciones, una de las cuales puede conducir a una falla (sería apropiado recordar
fuzzing aquí ). ¿Qué pasaría si pudiera escribir una sola prueba que verifique automáticamente un cierto método para 1000 combinaciones de sus entradas y descubra cuál? ¿El método responde incorrectamente? Las pruebas basadas en la verificación de la propiedad es exactamente lo que nosotros en una situación de este tipo es útil. Es decir, en el curso de este módulo de pruebas cheques, llamándola con todas las combinaciones posibles de los datos de entrada, lo que aumenta la probabilidad de encontrar algunos errores. Supongamos que tenemos un método
addNewProduct(id, name, isDiscount)
y Biblioteca realizando pruebas, lo llama con muchas combinaciones de parámetros de tipo numérico, de cadena y lógico, por ejemplo -
(1, "iPhone", false)
,
(2, "Galaxy", true)
. Es posible realizar pruebas basadas en la verificación de propiedades utilizando el entorno habitual de ejecución de pruebas (Mocha, Jest, etc.) y utilizando bibliotecas especializadas como
js- verified o
testcheck (esta biblioteca tiene muy buena documentación).
Consecuencias de la desviación de las recomendaciones
El desarrollador, inconscientemente, selecciona dichos datos de prueba que cubren solo aquellas partes del código que funcionan correctamente. Desafortunadamente, esto reduce la efectividad de las pruebas como medio para detectar errores.
Enfoque correcto
Probar muchas opciones de entrada utilizando la biblioteca mocha-testcheck.
require('mocha-testcheck').install(); const {expect} = require('chai'); const faker = require('faker'); describe('Product service', () => { describe('Adding new', () => {
▍8. Esforzarse por que el código de prueba sea autosuficiente, minimizando las ayudas externas y las abstracciones
Recomendaciones
Probablemente ahora sea obvio que estoy comprometido con pruebas extremadamente simples. El hecho es que, de lo contrario, el equipo de desarrollo de un determinado proyecto, de hecho, tiene que ocuparse de otro proyecto. Para entender su código, tienen que pasar un tiempo valioso, que no tienen tanto.
Está muy bien escrito sobre este fenómeno: “Un código de producción de alta calidad es un código bien pensado, y un código de prueba de alta calidad es un código completamente comprensible ... Cuando escriba una prueba, piense en quién verá el mensaje de error que él muestra. Esta persona no querría, para comprender las causas del error, leer el código de todo el conjunto de pruebas o el código del árbol de herencia de las utilidades utilizadas para las pruebas ".
Para que el lector entienda la prueba sin abandonar su código, minimice el uso de utilidades, ganchos o cualquier mecanismo externo al realizar la prueba. Si para hacer esto, necesita recurrir a copiar y pegar código con demasiada frecuencia, puede detenerse en un mecanismo auxiliar externo, cuyo uso no violará la comprensión de la prueba. Pero, si el número de tales mecanismos aumenta, el código de prueba perderá la comprensión.
Consecuencias de la desviación de las recomendaciones
, 4 , 2 , ? ! , , .
. ?
test("When getting orders report, get the existing orders", () => { const queryObject = QueryHelpers.getQueryObject(config.DBInstanceURL); const reportConfiguration = ReportHelpers.getReportConfig();
Enfoque correcto
, .
it("When getting orders report, get the existing orders", () => {
▍9. :
Recomendaciones
, , , , . . , (
) . — , ( , ). — , , , , . , , . — , , , , ( , , , ).
Consecuencias de la desviación de las recomendaciones
, . . . . , , , .
. , .
before(() => {
Enfoque correcto
, , .
it("When updating site name, get successful confirmation", async () => {
▍10. , . expect
Recomendaciones
, ,
try-catch-finally
catch
. , , , .
Chai,
expect(method).to.throw
. Jest:
expect(method).toThrow()
. , . , , .
Consecuencias de la desviación de las recomendaciones
, , , .
,
try-catch
.
it("When no product name, it throws error 400", async() => { let errorWeExceptFor = null; try { const result = await addNewProduct({name:'nest'});} catch (error) { expect(error.code).to.equal('InvalidInput'); errorWeExceptFor = error; } expect(errorWeExceptFor).not.to.be.null;
Enfoque correcto
expect
, , .
it.only("When no product name, it throws error 400", async() => { expect(addNewProduct)).to.eventually.throw(AppError).with.property('code', "InvalidInput"); });
▍11. ,
Recomendaciones
. , (smoke test), -, , . - . , , ,
#cold
,
#api
,
#sanity
. . , Mocha
-g
(
--grep
).
Consecuencias de la desviación de las recomendaciones
, , , , , , . .
Enfoque correcto
#cold-test
, . , -, , — .
▍12.
Recomendaciones
, Node.js-. , , Node.js .
TDD — , , . , . , ,
Red-Green-Refactor . , - , , , , , . , . ( — , , ).
Consecuencias de la desviación de las recomendaciones
— , . .
2.
▍13. ,
Recomendaciones
, , 10 , . , . , . , (, ), , , , ? - ?
, . , 2019 , , TDD, — , . , , ,
. , IoT-, , , - Kafka RabbitMQ, . - , , , . , , , , ? (, , Alexa) , , .
, ( ). , , , , , , . , , - API — Consumer-Driven Contracts . , , , , . , , , , , . , , .
, TDD . , TDD , . , , , .
Consecuencias de la desviación de las recomendaciones
— ( ), .
Enfoque correcto
.
.
, , Node.js, .
▍14. ,
Recomendaciones
. — . , , . , , - , , - ? , . , : TDD, — .
«». API, - , (, , , , , ). , , , (, ). , , , , , , .
Consecuencias de la desviación de las recomendaciones
, , , , 20.
Enfoque correcto
supertest , API, Express, .
API, Express▍15. , API, Consumer-Driven Contracts
Recomendaciones
, , , , . , , - , , , - . «-22» : . , , . ,
Consumer-Driven Contracts PACT .
. . PACT , ( «»). , , PACT, , , . , , , , .
Consecuencias de la desviación de las recomendaciones
.
Enfoque correcto
Consumer-Driven Contracts, , B , . B .
▍16.
Recomendaciones
(middleware) - , , - , Express-. . , , . , , JS-
{req,res}
. , «» (,
Sinon ) ,
{req,res}
. , , , .
node-mock-http , , , . , , HTTP-, -.
Consecuencias de la desviación de las recomendaciones
Express .
Enfoque correcto
Express-.
▍17.
Recomendaciones
, , , , , , . , , - . , , , ( , , ), , ( — ) . , :
SonarQube (
2600 GitHub)
Code Climate (
1500 ).
Consecuencias de la desviación de las recomendaciones
, , . . , .
Enfoque correcto
Code Climate.
Code Climate▍18. , Node.js
Recomendaciones
, , . , , , - , . , - , , , , ? , , ? , API ?
Netflix
- . , , , , . , - —
Chaos Monkey . , , , . Kubernetes —
kube-monkey . , Node.js? , , , V8 1.7 . .
node-chaos , -.
Consecuencias de la desviación de las recomendaciones
, , , .
Enfoque correcto
npm-
chaos-monkey , Node.js.
chaos-monkeyResumen
, Node.js-. , . .
Estimados lectores! - ?
