Este artículo merece su atención si Les apasiona escribir software de buena calidad y desean mejorar la estabilidad de su aplicación con pruebas. Está cansado de que aparezcan errores inesperados en sus sistemas de producción. Necesita ayuda para comprender qué son las pruebas automatizadas y cómo abordarlas. ¿Por qué necesitamos pruebas automatizadas? Como ingenieros, queremos , pero con cada nueva característica que creamos, inevitablemente aumentamos el tamaño y la complejidad de nuestras aplicaciones. crear cosas que funcionen A medida que el producto crece, cada vez lleva más tiempo probar (por ejemplo, con las manos) cada funcionalidad afectada por los cambios. manualmente La ausencia de pruebas automatizadas nos lleva a perder demasiado tiempo y reducir la velocidad de envío o a gastar muy poco para ahorrar velocidad, lo que genera nuevos errores en el trabajo pendiente junto con las llamadas nocturnas de PagerDuty. Por el contrario, . Entonces, las computadoras pueden programarse para hacer lo mismo repetidamente ¡deleguemos las pruebas a las computadoras! Tipos de pruebas La sugiere . Profundicemos en cada tipo y comprendamos por qué los necesitamos. idea de la pirámide de pruebas tres tipos principales de pruebas: unitarias, de integración y de un extremo a otro Pruebas unitarias Una es una pequeña pieza de lógica que se prueba de forma (sin depender de otros componentes). unidad aislada Terminan en segundos. les permite ejecutarlos en cualquier momento, localmente y en CI, sin activar los servicios dependientes ni realizar llamadas a API y bases de datos. Las pruebas unitarias son rápidas. El aislamiento una función que acepta dos números y los suma. Queremos llamarlo con diferentes argumentos y afirmar que el valor devuelto es correcto. Ejemplo de prueba unitaria: // Function "sum" is the unit const sum = (x, y) => x + y test('sums numbers', () => { // Call the function, record the result const result = sum(1, 2); // Assert the result expect(result).toBe(3) }) test('sums numbers', () => { // Call the function, record the result const result = sum(5, 10); // Assert the result expect(result).toBe(15) }) Un ejemplo más interesante es el componente React que muestra algo de texto una vez finalizada la solicitud de API. Necesitamos simular el módulo API para devolver los valores necesarios para nuestras pruebas, representar el componente y afirmar que el HTML renderizado tiene el contenido que necesitamos. // "MyComponent" is the unit const MyComponent = () => { const { isLoading } = apiModule.useSomeApiCall(); return isLoading ? <div>Loading...</div> : <div>Hello world</div> } test('renders loading spinner when loading', () => { // Mocking the API module, so that it returns the value we need jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Loading...').toBeInTheDocument() }) test('renders text content when not loading', () => { // Mocking the API module jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Hello world').toBeInTheDocument() }) Pruebas de integración Cuando su interactúa con otras , lo llamamos . Estas pruebas son más lentas que las pruebas unitarias, pero prueban cómo se conectan las partes de su aplicación. unidad unidades (dependencias) integración un servicio que crea usuarios en una base de datos. Esto requiere que una instancia de base de datos ( ) esté disponible cuando se ejecutan las pruebas. Probaremos que el servicio pueda crear y recuperar un usuario de la base de datos. Ejemplo de prueba de integración: dependencia import db from 'db' // We will be testing "createUser" and "getUser" const createUser = name => db.createUser(name) // creates a user const getUser = name => db.getUserOrNull(name) // retrieves a user or null test("creates and retrieves users", () => { // Try to get a user that doesn't exist, assert Null is returned const nonExistingUser = getUser("i don't exist") expect(nonExistingUser).toBe(null); // Create a user const userName = "test-user" createUser(userName); // Get the user that was just created, assert it's not Null const user = getUser(userName); expect(user).to.not.be(null) }) Pruebas de un extremo a otro Es una prueba cuando probamos la , donde todas sus dependencias están disponibles. Esas pruebas simulan mejor el comportamiento real del usuario y le permiten detectar en su aplicación, pero son el tipo de prueba . de un extremo a otro aplicación completamente implementada todos los posibles problemas más lento Siempre que desee ejecutar pruebas de un extremo a otro, debe aprovisionar toda la infraestructura y asegurarse de que haya proveedores externos disponibles en su entorno. querrás tenerlos para las funciones de tu aplicación. Sólo críticas flujo de inicio de sesión. Queremos ir a la aplicación, completar los datos de inicio de sesión, enviarla y ver el mensaje de bienvenida. Echemos un vistazo a un ejemplo de prueba de un extremo a otro: test('user can log in', () => { // Visit the login page page.goto('https://example.com/login'); // Fill in the login form page.fill('#username', 'john'); page.fill('#password', 'some-password'); // Click the login button page.click('#login-button'); // Assert the welcome message is visible page.assertTextVisible('Welcome, John!') }) ¿Cómo se elige qué tipo de prueba escribir? Recuerde que y . las pruebas de un extremo a otro son más lentas que las de integración las pruebas de integración son más lentas que las pruebas unitarias Si la característica en la que está trabajando es de misión crítica, considere escribir al menos una prueba (como verificar cómo funciona la funcionalidad de inicio de sesión al desarrollar el flujo de autenticación). de un extremo a otro Además de los flujos de misión crítica, queremos probar tantos casos extremos y varios estados de la función como sea posible. nos permiten probar cómo funcionan juntas las partes de la aplicación. Las pruebas de integración Los puntos finales deben realizar las operaciones, producir el resultado esperado y no generar errores inesperados. Es una buena idea realizar pruebas de integración para puntos finales y componentes del cliente. Los componentes del cliente deben mostrar el contenido correcto y responder a las interacciones del usuario como usted espera que respondan. Y finalmente, ¿cuándo deberíamos elegir ? Todas las funciones pequeñas que se pueden probar de forma aislada, como que suma los números, que representa la etiqueta , son excelentes candidatas para las pruebas unitarias. Las unidades son perfectas si sigues el enfoque . las pruebas unitarias sum Button <button> de desarrollo basado en pruebas ¿Que sigue? (pero empieza poco a poco) ¡Escribe algunas pruebas! que se adapte a su proyecto/idioma. Cada idioma tiene una biblioteca popular para realizar pruebas, como / para JavaScript, / de un extremo a otro (también usa JavaScript), para Java, etc. Instale un marco de prueba Jest Vitest Cypress Playwright JUnit Encuentre una pequeña función en su proyecto y escriba una prueba para ella. unitaria Escriba una prueba para alguna interacción componente/servicio-base de datos. de integración Elija un escenario crítico que pueda probarse rápidamente, como un flujo de inicio de sesión simple, y escriba una prueba para ello. de un extremo a otro Haga lo anterior una vez para comprender cómo funciona. Luego, hágalo nuevamente durante el trabajo de alguna función/error. Luego compártelo con tus colegas para que todos puedan escribir exámenes, ahorrar tiempo y dormir mejor por la noche. Recursos útiles: mi blog personal de La pirámide de pruebas prácticas Ham Vocke por Desarrollo basado en pruebas Martin Fowler