Jul 11, 2023
Si quiere entregar rápido, sus pruebas tienen la última palabra
Artículos de la página de inicio de InfoQ Si lo desea
Artículos de la página de inicio de InfoQ Si desea entregar rápido, sus pruebas tienen la última palabra
6 de junio de 2023 23 minutos de lectura
por
Jorge Fernández Rodriguez
revisado por
mate campbell
Creo que estará de acuerdo en que la ingeniería de software es especial en comparación con otras profesiones. Las cosas cambian drástica y rápidamente. Se requiere mucho poder mental solo para mantenerse actualizado.
Tal vez como consecuencia de ello, nos aferremos a algunas prácticas o ideas generales bien establecidas (aunque nos causen problemas o no encajen en algunos casos). Esas prácticas intentan cubrir la mayoría de los casos, pero no pueden cubrirlos todos. Sin embargo, estas prácticas nos dan consuelo. Necesitamos tener algo que no cambie, que se sienta seguro y que libere nuestra mente de la carga de pensar si realmente encaja o no. Entramos en modo piloto automático.
El problema con eso es que queremos que el desarrollo de software se comporte como una línea de montaje: una vez que se construye la línea de montaje, nunca la tocamos. Operamos de la misma manera todo el tiempo. Eso puede funcionar con nuestros carriles CI/CD por un tiempo, pero lamentablemente no siempre funciona bien con nuestro código.
¡Migra fácilmente a la nube e innova increíblemente rápido con Kalix! Cree microservicios y API de alto rendimiento, no se necesitan operaciones. Aprende más.
Incluso empeora porque a veces el mensaje se transmite tantas veces que pierde su esencia y en algún momento tomamos esa práctica como parte de nuestra identidad, la defendemos y no dejamos entrar diferentes puntos de vista. si aparentemente requieren más esfuerzo. Otras veces solo queremos encajar y no queremos sugerir nuevas ideas.
Cuando codificamos, debemos luchar contra eso y reflexionar en cada caso si la práctica se ajusta al escenario actual. Piense en las "mejores prácticas" como "mejores prácticas genéricas".
Un ejemplo de eso son las muchas formas en que se puede malinterpretar ágil. La esencia se ha perdido en algunos casos.
En ese artículo, argumento que la esencia de Agile a menudo se pierde porque muchas veces la implementación de Agile se enfoca en las cosas equivocadas. Por definición, algo ágil puede cambiar fácilmente de dirección y responder rápidamente a los cambios.
Intentamos lograr esta capacidad de respuesta con prácticas de diferente naturaleza: técnicas, como CI/CD (Continuous Integration/Continuous Deployment), y estratégicas, como el desarrollo en iteraciones. Sin embargo, a menudo nos olvidamos de la agilidad cuando nos ocupamos del núcleo del desarrollo de software: la codificación. Imagina preparar tu comida o postre favorito sin el ingrediente principal de la receta. Esto es lo que estamos haciendo cuando luchamos por la agilidad sin tener en cuenta el código.
Eso puede suceder porque mejorar el código suena aterrador y complicado o podría ser fácil meterse en madrigueras de conejo (todo esto se puede aliviar). Tal vez sea solo porque no es fácil ver el efecto negativo que tienen algunas soluciones en nuestra maniobrabilidad; convirtiendo los desarrollos futuros en una pesadilla: lo contrario de la agilidad. En lugar de centrarnos en el código, se pone demasiada atención en alcanzar la perfección en nuestros procesos (de metodologías como Scrum), que son menos importantes y tratan de resolver el problema sin abordar el problema principal.
Finalmente, sugiero que debemos dar más visibilidad al efecto que tiene el código en los desarrollos futuros (y como consecuencia, el futuro del negocio). Con suerte, la IA puede ayudarnos a cuantificar esto con algo así como un coeficiente que no solo nos diga la calidad, sino que también prediga cuánto más lento será el desarrollo en función de nuestras elecciones potenciales. Creo que algo como esto podría ayudar a las empresas a darse cuenta de que necesitan invertir en desarrollo sostenible. Las discusiones sobre cuándo abordar la deuda técnica pasarían a la historia.
En este artículo, me centraré en una práctica de codificación que a menudo no se cuestiona lo suficiente y que juega un papel vital en la agilidad: la forma convencional de realizar pruebas.
También presentaré "Inmunidad al cambio", una estrategia que descubrí recientemente que puede ayudarte a lograr objetivos y cambiar muchos hábitos; no solo hábitos de codificación sino también otros hábitos en su vida. Además, ilustra uno de los puntos que mencioné en mi artículo anterior: queremos lograr maniobrabilidad cambiando algunas prácticas/hábitos, pero inconscientemente nos saboteamos porque ignoramos el papel que juega la codificación para lograr ese objetivo.
Con suerte, estamos de acuerdo en que las (buenas) pruebas automatizadas nos brindan una gran ventaja: podemos cambiar el código y verificar rápidamente que ninguna funcionalidad existente se rompe. Las pruebas son nuestra red de seguridad.
Sin embargo, no son baratos, por lo que los costos deben compensarse, y esto solo sucede después de un tiempo. Cuantas más veces se ejecuta, más valor obtenemos de él. Pero si modificamos la prueba, el "contador de beneficios" de la prueba se restablece a cero.
Además de compensar los costos, debemos considerar otra cosa: cuando creamos una prueba, hacemos todo lo posible para garantizar que la prueba sea correcta, pero no podemos estar 100% seguros. Si pudiéramos estar seguros de que cualquier código que escribimos es correcto, ni siquiera necesitaríamos pruebas en primer lugar, ¿verdad?
Las pruebas solo nos dan confianza con el tiempo. Considere lo siguiente: imagine que cada vez que se ejecuta una prueba, nos brinda un punto de confianza. Si se ejecuta 1000 veces, ganamos 1000 puntos de confianza.
Si en algún momento descubrimos un error en la prueba, perdemos los 1000 puntos de confianza. Pensamos que nos estaba protegiendo, pero no fue así.
De manera similar, si necesitamos refactorizar la lógica verificada por la prueba y la prueba se rompe, no podemos estar seguros de que la lógica de negocios o la prueba sean correctas. También perdemos los puntos de "confianza" que acumulamos. Es como si estuviéramos construyendo una nueva red de seguridad.
Es una inversión en el futuro. Los seres humanos y las empresas rara vez piensan en inversiones a largo plazo, pero afortunadamente, los beneficios de las pruebas automatizadas son ampliamente aceptados (aunque de vez en cuando escucho historias de alguien que aún no adopta las pruebas automatizadas).
Lamentablemente, otras inversiones a largo plazo, como mantener el código flexible mediante refactorizaciones, no se adoptan tan ampliamente. Con suerte, las ideas nada innovadoras mencionadas en este artículo ayudan a eliminar las barreras para eso.
Cada vez que agrego una nueva funcionalidad, agradezco las pruebas que se han escrito, especialmente en servicios grandes con mucha funcionalidad. No puedo imaginar tener que verificar cada uno de ellos manualmente. Sería una tontería, considerando lo lento que sería.
Sin embargo, también pueden traer desventajas si no seguimos la estrategia adecuada para nuestro caso. Una estrategia de prueba inapropiada puede ralentizar la entrega y deteriorar la felicidad y la experiencia del desarrollador.
Descubrámoslos junto con las siguientes preguntas:
¿Todas sus pruebas unitarias verifican clases o métodos de forma aislada?
Si la respuesta es afirmativa y te estás burlando mucho de las dependencias, es posible que estés cansado de preparar un simulacro tras otro, para descubrir en algunos casos que el simulacro no era lo suficientemente real y que la lógica en realidad no funciona como debería. Con suerte, lo descubrirá antes de ir a producción y no tendrá que hacer muchos cambios.
¿A veces tiene la impresión de que las pruebas restringen la forma en que puede cambiar el código?
Supongo que esto también es un sí. A veces, el código existente ya no se ajusta a la nueva funcionalidad o se vuelve demasiado complicado. Decides refactorizarlo. Tarda 10 o 15 minutos porque es un cambio pequeño y BOOOOM, muchas pruebas ya no se compilan o fallan. Adaptar las pruebas y hacerlo lleva una cantidad de tiempo irrazonable, mucho más que adaptar el código. Terminarías pasando días para un cambio de 10 a 15 minutos. ¡¡¡¡¿¿¿POR QUÉ???!!!! El comportamiento no cambió.
Si la característica no se cambia, idealmente las pruebas no deberían romperse. Si adapta las pruebas, ¿puede volver a saltar a la red de seguridad? Y recuerda que el "contador de beneficios" y el "contador de confianza" de las pruebas se ponen a cero cuando las modificas.
Veremos más adelante que en muchos casos podemos evitar esta situación, y evitar añadir hacks. Porque los trucos nos dan la ilusión de que entregamos rápido, al menos inicialmente. Pero esta es una inversión de poco tiempo y nos devolverá el golpe: el código se vuelve rígido y después de algunos meses, dedicaremos mucho más tiempo a entender el código y cambiarlo. Incluso después de algunos días, no recordaremos qué hace el código. Y esto se pondrá cada vez peor, sin siquiera pensar en los pobres nuevos empleados que tienen aún menos contexto. Sin embargo, si su base de código está plagada de hacks, no tendrá que preocuparse demasiado por ellos, ya que probablemente no durarán mucho.
¿Su conjunto de pruebas tiene muchas pruebas de integración y e2e (extremo a extremo)?
Las pruebas de integración tienden a sobrevivir a más cambios que las pruebas unitarias, pero son mucho más lentas.
Si ha desarrollado muchas pruebas e2e, probablemente haya pasado por una experiencia dolorosa:
El dolor empeora si las pruebas e2e involucran muchos componentes conectados a través de la red (pero no construya una gran bola de barro para evitar esto).
Entonces, una mala estrategia de prueba por sí sola puede arruinar su entrega. Las pruebas unitarias pueden "evitarnos" escribir mejor código. No importa si tus rituales "ágiles" están en punto. Te apetece tener una camisa de fuerza (o más de una, si nos ponemos paranoicos añadiendo más y más pruebas, intentando estar 200% seguros). Mientras tanto, los dientes afilados de los competidores, que pueden adaptarse más rápido a los cambios en el océano del mercado, se acercan a nosotros, y la camisa de fuerza no te deja ni moverte. Tampoco serán de ayuda contra esos dientes.
Entonces, algunas preguntas clave que me gustaría que te hagas después de leer esto:
Veamos una simplificación de un escenario que veo a menudo. Una aplicación de back-end implementada usando Spring. Normalmente veo un mapeo 1-1 entre las clases de producción y prueba, como en la imagen:
[Click en la imagen para ver a tamaño completo]
La "clase con la lógica principal" a menudo se denomina "servicio". A veces, el servicio es específico por entidad de dominio, a veces el servicio representa un concepto más abstracto.
A veces, la "lógica principal" también se extiende a los componentes del marco, como los controladores o los oyentes. No solo la línea entre el marco y la lógica de negocios se vuelve borrosa, sino también la línea que distingue qué se puede probar mejor en pruebas unitarias o pruebas de integración. Como resultado, a veces veo escenarios similares en pruebas unitarias y pruebas de integración. Esto es inútil y costoso.
Tener una prueba unitaria por clase tiene sentido cuando las clases contienen una funcionalidad compleja. Cuando eso sucede, es difícil identificar los errores, por lo que tener pequeños fragmentos de código ayuda a localizar los problemas más rápido. Este es en realidad uno de los argumentos que respaldan las pruebas unitarias. Tenga en cuenta que estoy diciendo "funcionalidad compleja" y no "lógica compleja" porque siempre es posible implementar una funcionalidad simple de una manera compleja.
Ahora imagine que necesita agregar una característica o cambiar la lógica porque los requisitos cambiaron. Eso rara vez sucede, ¿verdad? ;). La implementación actual no se ajusta a la nueva idea o contexto. Como casi siempre hay dos opciones: truco sucio o refactorización. Por supuesto, siempre elegimos la refactorización:
[Click en la imagen para ver a tamaño completo]
Imagine que la división estaba limpia (solo mueva algunos métodos a la nueva clase) y la clase anterior mantiene una referencia a la nueva clase. ¿Tiene sentido dividir la clase de prueba para mantener el mapeo 1-1? ¿Qué ganamos? Podríamos mantener la prueba tal como está (solo se necesita un ligero cambio).
En el caso de que el refactor fuera más complejo, imagine que la lógica (detalles de implementación) se adaptó en 20 minutos. Como ya mencionamos, las pruebas no son tan rápidas de cambiar.
Respecto a las pruebas vemos dos cosas:
Vemos que cada tipo tiene algunos beneficios. Así que tal vez podamos combinar lo mejor de la integración y las pruebas unitarias convencionales. Tradicionalmente, una unidad es una clase o un método. Al menos así es como lo aprendí hace mucho tiempo. Puede parecer que la idea de no probar una clase de forma aislada es incorrecta. Pero tal vez el concepto de "unidad" podría beneficiarse de su propia "refactorización".
Durante mucho tiempo me pregunté por qué no veía a la gente hablando de esto. Después de un tiempo, encontré algunos artículos y videos que hablaban sobre el tema y obtuve una idea más clara de este y otros conceptos, como los diferentes tipos de dobles de prueba, cuando leí el libro Unit Testing Principles, Practices, and Patterns de Vladimir Khorikov.
Entonces, si no es una clase, ¿qué es una unidad? ¿Qué pasa con una parte de la funcionalidad, distribuida en varios métodos o clases? Podrías decir, "espera, eso suena como una prueba de integración". Bueno no exactamente. Tomaré prestadas algunas definiciones del libro:
Una prueba unitaria:
Una prueba de integración no cumple al menos uno de los criterios mencionados.
Una buena prueba unitaria:
Así que el concepto de "unidad" ahora parece más abstracto y más flexible. Además, en las pruebas de integración, podemos considerar muchas cosas, como pruebas del sistema, pruebas e2e, etc.
Entonces, vayamos a un nivel de granularidad más alto. Agrupemos la lógica inicial en un módulo y hagamos algunos cambios más:
[Click en la imagen para ver a tamaño completo]
Lo primero que hicimos fue agrupar la lógica del dominio en una forma relativamentepequeño módulo. No estoy seguro de si esto ayudará, pero piense en los módulos como un microservicio dentro de un microservicio. ¿Cuán pequeño? Como con casi todas las decisiones, no hay una sola regla a seguir. Tenga en cuenta que el ejemplo es una simplificación: puede haber varias entidades involucradas o ninguna, y puede haber más clases alrededor.
Es importante tener en cuenta las ventajas y desventajas:
ventajas:
Desventajas:
Tenga en cuenta que podría tener sentido tener una prueba específica para una funcionalidad realmente compleja.
Lo segundo que hicimos fue separar el dominio de los componentes del marco.
Una de las funciones de frameworks como Spring es unir diferentes elementos de una aplicación. Lo que queremos hacer aquí es similar a la arquitectura hexagonal, también conocida como puertos y adaptadores: controladores, escuchas, filtros, DAO u otras construcciones del marco son puertos que conectan la lógica del dominio (núcleo de la aplicación) con el mundo exterior. Idealmente, esos componentes no contienen conocimiento del dominio. Por ejemplo, los controladores, escuchas y filtros contienen exclusivamente una llamada a la lógica del dominio (y, si es necesario, otra llamada a la lógica que asigna los datos a algún formato más amigable para el modelo de dominio).
ventajas:
Una extensión de separar el dominio y la lógica del marco es mantener la lógica del dominio para cada funcionalidad cerca, para garantizar la cohesión. Toda la lógica relacionada con esta funcionalidad se implementa en este módulo, no se distribuye en varios módulos.
Ya experimenté una pesadilla, necesitaba excavar en el código base de un monolito para encontrar todos los lugares que necesitan cambiar. Hicimos una depuración intensiva durante meses y el cambio principal se realizó en dos días.
Solo necesitamos pruebas de integración para cualquier cosa que no se pruebe en pruebas unitarias, como otras funciones del marco: configuración de punto final, serialización, deserialización de datos y errores, acceso a datos, llamadas remotas, autenticación. Una prueba de humo para el caso feliz también podría ser interesante.
El último punto que quiero mencionar es con respecto a las pruebas e2e. e2e son MUCHO MÁS caras que las pruebas de integración, así que utilícelas con cuidado:
Una alternativa a las pruebas e2e para esos escenarios no críticos podría ser utilizar pruebas basadas en contratos.
Puedo recordar varios casos en los que este enfoque me habría ayudado. El más significativo fue un momento en el que tuvimos que desarrollar un algoritmo para un sistema de licitación.
La primera implementación del algoritmo inicialmente se veía bien, pero comenzamos a descubrir escenarios complicados cada semana después del lanzamiento. Empezamos a corregirlos individualmente y la implementación pronto se volvió demasiado compleja y terminamos teniendo un incidente.
Después de eso, lo repasamos de nuevo y encontramos un enfoque diferente. Terminamos con un 75 % menos de código y la implementación fue mucho más simple. Esa fue la parte buena.
La parte triste es que las pruebas unitarias verificaron muchos métodos individualmente y teníamos un montón de ellos. Si hubiéramos probado la lógica como una unidad de funcionalidad, nos hubiéramos ahorrado 12 días de trabajo (de 15) y mucha frustración provocada por volver a hacer todas las pruebas.
Entonces, además de todas las ventajas mencionadas, este enfoque de prueba puede aumentar su felicidad como desarrollador/equipo. Otras estrategias que apuntan a entregar más rápido requieren convencer a personas fuera de su entorno más cercano, y eso puede ser muy difícil. Este está todo en tus manos.
En resumen:
Y ten en cuenta que:
No estoy diciendo que probar de esta manera sea bueno para todos y todo el tiempo. Debe evaluar si esto podría ayudarlo y en qué casos. Como mencioné, puede ser especialmente útil en entornos ágiles, donde hay mucha experimentación, el dominio se crea y evoluciona en iteraciones y se incorporan nuevas funcionalidades regularmente. Muchas de esas acciones se beneficiarían de refactorizaciones.
Algunas de estas ventajas también se pueden conseguir con TDD (test-driven development) si lo hacemos conscientemente. Lamentablemente, una vez más, tendemos a centrarnos demasiado en la herramienta o técnica sin comprender la esencia y lo que originalmente se pretendía lograr, por lo que no sacamos nada de ella y terminamos abandonándola.
Adoptar prácticas como esta requiere cambiar hábitos y sabemos lo difícil que puede ser. Pensamos que la fuerza de voluntad es lo que necesitamos, pero no es suficiente. Quiero compartir un enfoque que descubrí recientemente. Se describe en el libro Inmunidad al cambio: cómo superarlo y desbloquear el potencial en usted mismo y en su organización, por Lisa Lahey, Ed.D. y Robert Kegan, Ph.D., miembros de la Escuela de Graduados en Educación de Harvard.
A menudo tratamos de implementar cambios solo a través de la fuerza de voluntad. Su teoría es que esto puede no funcionar porque tenemos algo parecido a un "sistema inmunológico" que trabaja en contra de los cambios, saboteando todos nuestros intentos. Entonces, para adoptar un cambio, primero debemos descubrir ese "sistema inmunológico" y trabajar en sus raíces.
Este "sistema inmunológico" fue desarrollado en el pasado. Con el tiempo, tratamos de alcanzar nuestras metas. Para conseguirlos, seguimos una serie de pasos:
Esos conceptos forman la base del proceso que implementaron. Voy a describir los pasos muy brevemente:
Si encontramos que los supuestos ya no son válidos, trabajamos en ellos. Eso hace que sea más fácil adoptar el cambio.
En el podcast "Dare to Lead", Lisa Lahey y Brené Brown pusieron en práctica Immunity to change a través de un ejemplo de la propia vida de Brené. Tiene dos partes (parte uno y parte dos). A menudo escucho podcasts mientras realizo otras tareas, pero valió la pena escucharlo con mucha atención.
El podcast comienza con una poderosa pregunta de Brené: "¿Por qué todos queremos transformarnos y nadie quiere cambiar?Continúan hablando de cómo tendemos a asociar la falta de cambio con no quererlo lo suficiente o con intenciones fingidas. Mencionan que las intenciones no lo son todo, porque las personas cuya vida está en riesgo y que quieren vivir, todavía a veces no logran cambiar.
Luego trabajan en un ejemplo, que probablemente nos sea familiar: Brené dice que quiere ser más disciplinada con el equipo acerca de tener reuniones periódicas.
Lisa guía a Brené con algunas preguntas para ayudarla a completar las columnas. Para Brené es clave para hacer su vida más fácil y alcanzar el éxito. A pesar de eso, y de que es algo que ella misma puede cambiar por sí misma, no fue capaz de implementar el cambio.
Brené luego se sorprende cuando descubre los compromisos, las suposiciones y las preocupaciones no expresadas que la están saboteando y que la llevaron a la situación actual. Dice que en el pasado aprendió que la disciplina y la creatividad se excluyen mutuamente y por eso cree que con reuniones periódicas perderá tiempo para lo que más disfruta: tiempo para ser creativa. Así que se salta esas reuniones, pero como consecuencia termina teniendo muchas reuniones únicas. Porque quiere demostrar que es una líder accesible, dice. Esto la ha ayudado a alcanzar muchas metas, pero ahora dedica demasiado tiempo a eso y se está dando cuenta de que tener reuniones periódicas no empeorará las cosas, pero podría ahorrarle mucho tiempo.
Al final, Lisa sugiere que necesita encontrar formas de validar esta nueva creencia de que la disciplina y la creatividad son compatibles. El objetivo es crear nuevas vías neuronales y anular las antiguas que se han tallado durante años.
Creo que podemos beneficiarnos de esta estrategia de dos maneras: este sabotaje de nuestro "sistema inmunológico" ilustra lo que estamos haciendo cuando tratamos de ser ágiles mientras ignoramos cómo se ve nuestro código. Creo que esto se puede aplicar en muchos aspectos de nuestras vidas:
Si, por ejemplo, tomamos el caso de cambiar a esta nueva estrategia de prueba como ejemplo, necesitaríamos averiguar qué queremos lograr con el cambio y por qué es más importante que otras cosas. Para mí, en mi contexto actual, la flexibilidad es fundamental.
Con respecto a lo que podríamos estar haciendo que va en contra de la meta, por qué lo hacemos y las suposiciones que hicimos que activan esas actividades, podríamos pensar que nuestra vida podría ser más fácil si lo hacemos de la manera habitual porque solo necesitamos continuar por inercia. También encontramos razones para no hacerlo: "Esta es la forma en que siempre hemos hecho las pruebas y cómo nos enseñaron a hacerlo. Todo el mundo lo está haciendo de esta manera, por lo que tiene que ser correcto".
Entonces, para combatir eso, necesitamos expresar los efectos secundarios negativos y reescribir esas creencias, luego encontrar una manera de probar que las suposiciones son incorrectas y finalmente implementar el cambio.
Si desea obtener más detalles, puede consultar los recursos vinculados en el podcast Atrévete a liderar mencionado anteriormente (parte uno y parte dos), su sitio web o su libro.
Escribir para InfoQ ha abierto muchas puertas y ha aumentado las oportunidades profesionales. para mí. Pude interactuar profundamente con expertos y líderes de opinión para aprender más sobre los temas que cubrí. Y también puedo difundir mis aprendizajes a la comunidad tecnológica en general y comprender cómo se utilizan las tecnologías en el mundo real.
¡Descubrí el programa de colaboradores de InfoQ a principios de este año y lo he disfrutado desde entonces! Además de brindarme una plataforma para compartir el aprendizaje con una comunidad global de desarrolladores de software, el sistema de revisión entre pares de InfoQ ha mejorado significativamente mi escritura. . Si está buscando un lugar para compartir su experiencia en software, comience a contribuir con InfoQ.
Empecé a escribir noticias para la cola de InfoQ .NET como una forma de mantenerme al día con la tecnología, pero aproveché mucho más. Conocí a gente bien informada, obtuve visibilidad global y mejoré mis habilidades de escritura..
Convertirse en editor de InfoQ fue una de las mejores decisiones de mi carrera. . Me ha desafiado y me ha ayudado a crecer de muchas maneras. . Nos encantaría tener más gente.Unete a nuestro equipo.
InfoQ busca un editor en jefe de tiempo completo para unirme al equipo internacional, siempre remoto, de C4Media. ¡Únase a nosotros para cubrir las tecnologías más innovadoras de nuestro tiempo, colabore con los profesionales de software más brillantes del mundo y ayude a más de 1,6 millones de equipos de desarrollo a adoptar nuevas tecnologías y prácticas que amplían los límites de lo que el software y los equipos pueden ofrecer!
Todos los martes se envía un resumen del contenido de InfoQ de la semana pasada. Únase a una comunidad de más de 250 000 desarrolladores sénior. Ver un ejemplo
Protegemos su privacidad.
Debe registrar una cuenta de InfoQ o iniciar sesión o iniciar sesión para publicar comentarios. Pero hay mucho más detrás de estar registrado.
Aproveche al máximo la experiencia de InfoQ.
HTML permitido: a,b,br,blockquote,i,li,pre,u,ul,p
HTML permitido: a,b,br,blockquote,i,li,pre,u,ul,p
HTML permitido: a,b,br,blockquote,i,li,pre,u,ul,p
Únete a una comunidad de expertos. ¿Todas sus pruebas unitarias verifican clases o métodos de forma aislada? ¿A veces tiene la impresión de que las pruebas restringen la forma en que puede cambiar el código? ¿Su conjunto de pruebas tiene muchas pruebas de integración y e2e (extremo a extremo)? pequeño ¿Por qué todos queremos transformarnos y nadie quiere cambiar? Jorge Fernández Rodríguez ha abierto muchas puertas y aumentado las oportunidades de carrera. El sistema de revisión entre pares de Vivian Hu InfoQ ha mejorado significativamente mi escritura. Oghenevwede Emeni obtuvo visibilidad global y mejoró mis habilidades de escritura. Edin Kapić Las mejores decisiones de mi carrera me ayudaron a crecer en muchos Formas de unirse a nuestro equipo Thomas Betts Editor en jefe de tiempo completo The InfoQ Aproveche al máximo la experiencia de InfoQ.