Pregunta

¿Qué puedo perder por la adopción de diseño basado en pruebas?

Lista de sólo los negativos;no lista de beneficios por escrito en una forma negativa.

¿Fue útil?

Solución

Varios inconvenientes (y yo no estoy diciendo que no hay beneficios - especialmente cuando la escritura de la fundación de un proyecto que iba a ahorrar un montón de tiempo en el final):

  • Gran inversión de tiempo. Por el simple caso de perder el 20% de la aplicación real, pero para los casos más complejos perder mucho más.
  • Una Complejidad Adicional. Para los casos complejos que los casos de prueba son más difíciles de calcular, te sugiero que en casos como el que probar y utilizar el modo automático, el código de referencia que se ejecuta en paralelo en la versión de depuración / marcha de prueba, en lugar de la prueba de la unidad de los casos más simples.
  • Diseño De Impactos. A veces el diseño no es claro desde el inicio y evoluciona a medida que avanza - esto te obligará a rehacer su prueba, lo que generará un gran tiempo que perder.Yo sugeriría posponer las pruebas de unidad en este caso hasta que usted tiene cierta comprensión del diseño en mente.
  • Continua Afinando. Para las estructuras de datos y la caja negra de los algoritmos de la unidad de pruebas sería perfecto, pero para los algoritmos que tienden a ser cambiado, ajustado o bien afinado, esto puede causar una gran inversión de tiempo que uno podría reclamar que no está justificada.Para utilizar cuando usted piensa que realmente se ajusta a la del sistema y no de la fuerza que el diseño se ajuste a TDD.

Otros consejos

Si usted quiere hacer "real" TDD (leer:prueba primero con el rojo, verde, refactorizar pasos), a continuación, usted también tiene que empezar a usar mocks/comprobantes de pago, cuando se desea probar los puntos de integración.

Cuando empiece a usar mocks, después de un tiempo, usted querrá empezar a usar la Inyección de dependencias (DI) y una Inversión de Control (IoC) el envase.Para hacer que usted necesita para utilizar interfaces para todo (que tiene un montón de trampas a sí mismos).

Al final del día, usted tiene que escribir mucho más código, que si usted acaba de hacer el "llano vieja manera".En lugar de sólo una clase de cliente, usted también necesita escribir una interfaz, un simulacro de clase, algunas de configuración de IoC y un par de pruebas.

Y recuerde que el código de la prueba también debe ser mantenido y cuidado.Las pruebas deben ser tan legible como todo lo demás y se necesita tiempo para escribir buen código.

Muchos desarrolladores no entiendo muy bien cómo hacer todo esto el "camino correcto".Pero porque todo el mundo les dice que TDD es el único y verdadero camino para el desarrollo de software, que sólo intenta lo mejor que puede.

Es mucho más difícil de lo que uno podría pensar.A menudo los proyectos se hagan con TDD terminar con un montón de código que nadie entiende realmente.La unidad de pruebas, a menudo, la prueba de lo malo, de la manera equivocada.Y nadie se acuerda de cómo una buena prueba debe ser similar, ni siquiera los llamados gurús.

Todas esas pruebas hacen que sea mucho más difícil "cambiar" (frente a la refactorización) el comportamiento de su sistema y simple de los cambios sólo se vuelve demasiado difícil y consume mucho tiempo.

Si usted lee el TDD literatura, siempre hay algunos muy buenos ejemplos, pero a menudo en la vida real, usted debe tener una interfaz de usuario y una base de datos.Aquí es donde TDD se vuelve muy difícil, y la mayoría de las fuentes no ofrecen buenas respuestas.Y si lo hacen, siempre involucra a más de abstracciones:burlarse de los objetos, la programación de una interfaz, MVC/MVP de patrones, etc., que volver a exigir una gran cantidad de conocimientos, y...usted tiene que escribir aún más el código.

Así que ten cuidado...si usted no tiene un equipo entusiasta y al menos un desarrollador experimentado que sabe cómo escribir buenos ensayos y también sabe un par de cosas acerca de la buena arquitectura, usted realmente tiene que pensar dos veces antes de bajar el TDD carretera.

Al llegar al punto donde usted tiene un gran número de pruebas, cambiando el sistema podría requerir la re-escritura de todos o algunos de sus exámenes, dependiendo de cuáles consiguió invalidada por los cambios.Esto podría a su vez una relativamente rápida modificación en un tiempo devorador.

También, usted puede iniciar la toma de las decisiones de diseño se basa más en TDD que en realidad el buen diseño prinicipals.Mientras que usted puede haber tenido un muy simple, fácil solución que es imposible probar el modo TDD demandas, ahora tiene un sistema mucho más complejo que en realidad es más propenso a errores.

Creo que el mayor problema para mí es la ENORME pérdida de tiempo que sea necesario "llegar a ti".Todavía estoy muy al comienzo de mi viaje con TDD (Ver mi blog para las actualizaciones de mis pruebas de aventuras si usted está interesado) y yo, literalmente, han pasado horas introducción.

Se necesita mucho tiempo para llegar a su cerebro en "modo de prueba" y de la escritura de código comprobable" es una habilidad en sí misma.

TBH, discrepo respetuosamente con Jason Cohen comentarios en la fabricación de los métodos privados público, que no lo es. No tengo la más métodos públicos en mi nueva forma de trabajar que antes.Sin embargo, no implican cambios en la arquitectura y permite para que usted "hot plug" módulos de código para hacer todo lo demás más fácil de probar.Usted debe no hacer las interioridades de que el código sea más accesible para hacer esto.De lo contrario, estamos de nuevo al cuadrado uno con todo lo que se pública, donde es la encapsulación en que?

Por lo tanto, (OMI) en una cáscara de nuez:

  • La cantidad de tiempo necesario para pensar (es decir,en realidad grok ing pruebas).
  • El nuevo conocimiento se requiere de saber cómo escribir código comprobable.
  • La comprensión de los cambios en la arquitectura necesaria para hacer que el código sea comprobable.
  • El aumento de su habilidad de "TDD-Coder" tratando de mejorar todas las otras habilidades requeridas para nuestro glorioso de programación de manualidades :)
  • La organización de su base de código para incluir el código de la prueba sin atornillar el código de producción.

PS:Si desea enlaces a positivos, he preguntado y respondido a varias preguntas sobre ella, echa un vistazo a mi perfil.

En los pocos años que he estado practicando Test Driven Development, yo tendría que decir que los mayores inconvenientes son:

Vendiendo a la gestión de

TDD es mejor hacerlo en parejas.Para uno, es difícil resistir la tentación de escribir la aplicación cuando SABER cómo escribir una if/else declaración.Pero un par se mantendrá en la tarea, ya que te acuerdes de él en la tarea.Lamentablemente, muchas empresas y/o directivos, no creo que este es un buen uso de los recursos.¿Por qué pagar para que dos personas escriban una característica, cuando tengo dos características que deben realizarse al mismo tiempo?

Venta de otros desarrolladores

Algunas personas simplemente no tienen la paciencia para escribir pruebas unitarias.Algunos están muy orgullosos de su trabajo.O, algunos simplemente les gusta ver a los enrevesado métodos/funciones de purga fuera de la final de la pantalla.TDD no es para todos, pero realmente me gustaría que fuera.Sería mantener las cosas mucho más fácil para esas pobres almas que heredan de código.

Mantener el código de prueba junto con su código de producción

Idealmente, las pruebas sólo se romperá cuando usted hace una mala código de decisión.Es decir, se pensaba que el sistema funcionaba de una manera, y resulta que no.Por la ruptura de una prueba, o una (pequeña) conjunto de pruebas, esto es realmente una buena noticia.Usted sabe exactamente cómo el nuevo código va a afectar el sistema.Sin embargo, si las pruebas están mal escritos, acoplado, o, peor aún, generados (tos VS Prueba), luego para el mantenimiento de sus pruebas, puede convertirse en un coro rápidamente.Y, después de suficientes pruebas de empezar a causar más trabajo que el valor percibido que están creando, a continuación, las pruebas será el primero a ser eliminado cuando los horarios se comprimen (por ejemplo.se pone a la hora de la verdad)

Los exámenes de escritura para que se cubra todo (100% de cobertura de código)

Idealmente, una vez más, si usted se adhiere a la metodología, el código será el 100% probado por defecto.Normalmente, el pensamiento, termino con el código de la cobertura de más del 90%.Esto sucede generalmente cuando tengo una plantilla de estilo de la arquitectura, y la base está probado, y tengo que tratar de cortar las esquinas y no probar la plantilla de las personalizaciones.También, he encontrado que cuando me encuentro con una nueva barrera que no me había encontrado anteriormente, tengo una curva de aprendizaje en las pruebas.Voy a admitir a escribir algunas líneas de código de la vieja escuela manera, pero realmente me gustaría tener que 100%.(Supongo que era una más triunfador en la escuela, er skool).

Sin embargo, con que yo digo que los beneficios de TDD mucho superan a los negativos por la simple idea de que si se puede lograr un buen conjunto de pruebas que cubren su aplicación, pero no son tan frágiles que un cambio rompe todos ellos, usted será capaz de mantener la adición de nuevas características en día 300 de su proyecto, como hizo en el día 1.Esto no sucede con todos aquellos que tratan de TDD pensando que se trata de una solución mágica para todos sus plagada de errores de código, y por eso creen que no pueden trabajar, y punto.

Personalmente he encontrado que con TDD, escribo más simple código, me dedique menos tiempo a debatir si una determinada solución de código funcionará o no, y que no tengo ningún miedo a cambiar ninguna línea de código que no cumplan con los criterios establecidos por el equipo.

TDD es una dura disciplina para dominar, y he estado trabajando durante un par de años, y todavía aprender nuevas técnicas de prueba todo el tiempo.Es una gran inversión de tiempo por delante, pero en el largo plazo, la sostenibilidad será mucho mayor que si no tenía pruebas unitarias automatizadas.Ahora, si mis jefes se podría entender esto.

En su primer TDD proyecto hay dos grandes pérdidas, el tiempo y la libertad personal

Se pierde tiempo porque:

  • La creación de una integral, refactorizar, fácil de mantener suite de pruebas de unidad y aceptación agrega mayor tiempo para la primera iteración del proyecto.Esto puede ser un ahorro de tiempo en el largo plazo, pero igualmente puede ser tiempo usted no tiene de sobra.
  • Usted necesita elegir y convertirse en experto en un conjunto básico de herramientas.Herramienta de prueba de unidad necesita ser complementado por algún tipo de marco de trabajo ficticios y que ambos necesitan para convertirse en parte de su generación automática del sistema.Usted también quiere recoger y generar la medición adecuada.

Perder la libertad personal porque:

  • TDD es una muy disciplinado manera de escribir código que tiende a frotar raw en contra de aquellos en la parte superior y la parte inferior de la escala de habilidades.Siempre se escribe el código de producción, en cierto modo, y someter su trabajo a una continua revisión por pares puede enloquecer a sus peores y mejores desarrolladores, e incluso conducir a la pérdida de la plantilla.
  • La mayoría de los métodos Ágiles, que se incorporan a TDD requiere que usted hable con el cliente continuamente acerca de lo que usted se propone lograr (en esta historia/día/lo que sea) y lo que las compensaciones son.Una vez más, esto no es de todos taza de té, tanto en los desarrolladores lado de la valla y los clientes.

Espero que esto ayude

TDD requiere de usted para planificar cómo las clases se operan antes de escribir código para pasar esas pruebas.Esto es a la vez una ventaja y un inconveniente.

Me resulta difícil escribir las pruebas en un "vacío", antes de cualquier código que se ha escrito.En mi experiencia, tienden a tropezar con mis pruebas siempre inevitablemente, tengo que pensar en algo mientras escribía mis clases, que olvidé al escribir mis pruebas iniciales.Entonces es el momento no sólo para refactorizar mis clases, pero TAMBIÉN mis pruebas.Repita esto tres o cuatro veces y puede ser frustrante.

Yo prefiero escribir un borrador de mis clases de primera, a continuación, escribir (y mantener) una batería de pruebas de unidad.Después tengo un proyecto, TDD funciona muy bien para mí.Por ejemplo, si un error es reportado, voy a escribir una prueba para explotar ese error y, a continuación, corregir el código para la prueba pasa.

La creación de prototipos puede ser muy difícil con TDD - cuando no estás seguro de qué camino vas a tomar a una solución, la escritura de las pruebas iniciales puede ser difícil (salvo los muy amplio).Esto puede ser un dolor.

Honestamente no creo que para "núcleo de desarrollo" para la gran mayoría de los proyectos no hay ninguna desventaja real, aunque;se habló mucho más de lo que debería ser, generalmente, por personas que creen que su código es lo suficientemente bueno, que no necesitan pruebas (nunca lo es) y personas que simplemente no puede ser molestado en escribir.

Bueno, y este estiramiento, se necesita depurar sus pruebas.También, hay un cierto costo en tiempo para la escritura de las pruebas, a pesar de que la mayoría de las personas están de acuerdo que es una inversión que se amortiza durante la vida útil de la aplicación, tanto en ahorro de tiempo de depuración y en la estabilidad.

El mayor problema que he tenido con ella, sin embargo, es llegar hasta la disciplina para escribir las pruebas.En un equipo, especialmente con un equipo consolidado, puede ser difícil convencerlos de que el tiempo invertido vale la pena.

Si las pruebas no son muy completa que pueda caer en una falsa sensación de "todo funciona" sólo porque usted fase de pruebas.En teoría, si su fase de pruebas, el código de trabajo;pero si podía escribir código perfectamente la primera vez que no necesita pruebas.La moraleja aquí es que asegúrese de comprobar en su propia antes de llamar a algo completo, no sólo se basan en las pruebas.

En esa nota, si tu cordura de verificación se encuentra algo que no está probado, asegúrese de volver y escribir una prueba para ella.

La desventaja de TDD es que normalmente está estrechamente asociada con la 'Ágil' metodología, lo que coloca a no importancia en la documentación de un sistema, sino más bien la comprensión de por qué una prueba de 'debe' devolver un valor específico en lugar de cualquier otro reside sólo en que el desarrollador de la cabeza.

Tan pronto como el desarrollador de hojas o se olvida la razón por la que la prueba devuelve un valor específico y no de otros, estás jodido.TDD es buena SI está adecuadamente documentado y rodeado por legible por humanos (es decir.punta de pelo manager) documentación que pueden ser contemplados en los 5 años cuando el mundo cambia y su aplicación necesita así.

Cuando hablo de la documentación, esto no es una propaganda en el código, esto es la escritura oficial que existe externos a la aplicación, tales como casos de uso y la información de fondo que pueden ser contemplados por los directivos, abogados y el pobre diablo que tiene que actualizar su código en 2011.

Me he encontrado con varias situaciones donde TDD me vuelve loco.Para nombrar algunos:

  • Caso de prueba de la capacidad de mantenimiento:

    Si estás en una gran empresa, muchas de las ocasiones son que usted no tiene que escribir los casos de prueba a ti mismo o al menos la mayoría de ellos están escritos por alguien que al entrar en la compañía.Una aplicación de las funciones de los cambios de vez en cuando y si usted no tiene un sistema en el lugar, tales como HP Quality Center, para el seguimiento de los mismos, vas a poner loco en ningún momento.

    Esto también significa que va a tomar nuevos miembros del equipo una buena cantidad de tiempo para agarrar lo que está pasando con los casos de prueba.A su vez, esto puede ser traducida en más dinero necesario.

  • Automatización de prueba de la complejidad:

    Si usted automatizar algunos o todos de los casos de prueba en la máquina ejecutable de secuencias de comandos de prueba, usted tendrá que asegurarse de que estas secuencias de comandos de prueba están en sintonía con sus correspondientes casos de prueba manual y en línea con los cambios en la aplicación.

    También, pasar el tiempo para depurar los códigos que ayudan a capturar insectos.En mi opinión, la mayoría de estos errores provienen del equipo de pruebas del fracaso para reflejar los cambios en la aplicación en la automatización de la secuencia de comandos de prueba.Cambios en la lógica de negocio, interfaz de usuario y otras interna cosas pueden hacer los scripts detener la ejecución o la ejecución de un mal funcionamiento.A veces los cambios son muy sutiles y difíciles de detectar.Una vez que todos mis scripts informe de falla debido a que se basa su cálculo en la información de la tabla 1, mientras que en la tabla 1 se ahora la tabla 2 (porque alguien cambió el nombre de los objetos de la tabla en el código de la aplicación).

El mayor problema son las personas que no saben cómo escribir apropiada de pruebas de unidad.Escribir pruebas que dependen unos de otros (y son excelentes para correr con Ant, pero luego de repente un error cuando ejecuto desde Eclipse, simplemente porque se ejecutan en un orden diferente).Escribir pruebas que no prueba nada en particular - que acaba de depurar el código, comprobar el resultado, y el cambio a prueba, que calificó de "prueba1".Se puede ampliar el ámbito de aplicación de métodos y clases, sólo porque será más fácil escribir pruebas unitarias para ellos.El código de la unidad de pruebas es terrible, con todos los clásicos problemas de programación (pesado de acoplamiento, los métodos que son 500 líneas de largo, valores codificados, la duplicación de código) y es un infierno para mantener.Por alguna extraña razón la gente a tratar de pruebas de unidad como algo inferior a la de los "real" de código, y a ellos no les importa en absoluto su calidad.:-(

Se pierde una gran cantidad de tiempo dedicado a la escritura de ensayos.Por supuesto, esto puede ser salvado por el final del proyecto por parte de la captura de los errores más rápido.

Se pierde la capacidad para decir que son "de hecho" antes de las pruebas de todos los de su código.

Se pierde la capacidad para escribir cientos o miles de líneas de código antes de ejecutarlo.

Se pierde la oportunidad de aprender a través de la depuración.

Se pierde la flexibilidad para enviar el código que usted no está seguro de.

Se pierde la libertad para bien de la pareja de sus módulos.

Pierdes la opción de saltar a escribir bajo el nivel de la documentación de diseño.

Se pierde la estabilidad que viene con el código que todo el mundo tiene miedo a cambiar.

Pierde el título de "hacker".

La mayor desventaja es que si usted realmente quiere hacer TDD correctamente usted tendrá que fallar mucho antes de tener éxito.En vista de cómo muchas empresas de software de trabajo (en dólares por KLOC) finalmente despedido.Incluso si el código es más rápido, más limpio, más fácil de mantener y tiene menos errores.

Si estás trabajando en una empresa que te paga por el KLOCs (o requisitos implementado -- incluso si no probado) manténgase alejado de TDD (o revisiones de código, o un par de programación, o de Integración Continua, etc.etc.etc.).

En la segunda la respuesta sobre el tiempo de desarrollo inicial.También se pierde la capacidad para trabajar cómodamente sin la seguridad de las pruebas.También he sido descrita como una TDD nutbar, por lo que podría perder un par de amigos ;)

La reorientación de las difíciles, las necesidades imprevistas es la constante pesadilla para el programador.Test-driven development te obliga a centrarse en la ya conocida, mundano requisitos, y los límites de su desarrollo a lo que ya se ha imaginado.

Piensa acerca de ello, es probable que terminan como el diseño específico de los casos de prueba, por lo que no se creativo y empezar a pensar "sería genial si el usuario podría hacer X, y y Z".Por lo tanto, cuando el usuario comienza a obtener todos entusiasmados con el potencial de enfriar los requisitos de X, y y Z, su diseño puede ser demasiado rígido centrado en la ya especificada casos de prueba, y será difícil de ajustar.

Esto, por supuesto, es una espada de doble filo.Si vas a gastar todo su tiempo en el diseño para todas las posibles, imaginables, X, Y, y Z que un usuario podría desear, inevitablemente, nunca se termina nada.Si usted completar algo, será imposible para cualquier persona (incluyendo usted) para tener alguna idea de lo que estás haciendo en tu código/diseño.

Es percibido como más lento.A largo plazo eso no es cierto en cuanto a la pena que te guarde en el camino, pero usted va a terminar de escribir más código de modo que podría decirse que se está gastando el tiempo en "la prueba no codificante".Es un error en el argumento, pero no pregunte!

Puede ser difícil y consumir mucho tiempo de escribir pruebas para "random" de datos como XML feeds y bases de datos (no es difícil).He pasado algún tiempo últimamente trabajando con datos meteorológicos alimenta.Es bastante confuso pruebas de redacción para que, al menos como yo no tengo demasiada experiencia con TDD.

Usted perderá grandes clases con varias responsabilidades.También puedes perder grandes métodos con múltiples responsabilidades.Usted puede perder cierta capacidad para refactorizar, pero también se pierde parte de la necesidad de refactorizar.

Jason Cohen dijo algo así como:TDD requiere una cierta organización de su código.Esto podría ser arquitectónicamente mal;por ejemplo, desde los métodos privados no pueden ser llamadas fuera de una clase, tiene que hacer que los métodos no-privado para hacerlos comprobables.

Digo esto indica una perdida de abstracción -- si el código privado realmente necesita ser probado, probablemente debería estar en una clase separada.

Dave Mann

Usted tiene que escribir aplicaciones de una manera diferente:uno que hace comprobables.Te sorprendería lo difícil que es a primera.

Algunas personas encuentran que el concepto de pensar acerca de lo que van a escribir antes de escribir demasiado difícil.Conceptos tales como la burla puede ser difícil para algunos demasiado.TDD en el legado de aplicaciones puede ser muy difícil si no estaban diseñados para las pruebas.TDD alrededor de los marcos de trabajo que no son TDD amable también puede ser una lucha.

TDD es una habilidad para junior devs pueden luchar en primera (principalmente porque no se les ha enseñado a trabajar de esta manera).

En general, aunque las contras convertido resuelto como las personas se convierten en expertos y abstracción de la 'mal olor' código y tener un sistema más estable.

Se necesita algún tiempo para entrar en eso y un poco de tiempo para comenzar a hacerlo en un proyecto, pero...Siempre me arrepiento de no hacer un Test Driven enfoque cuando me encuentro tontos errores que una prueba automatizada podría haber encontrado muy rápido.Además, TDD mejora de la calidad del código.

  • la unidad de prueba son más código para escribir, por lo tanto un mayor costo inicial de desarrollo
  • es más código para mantener
  • de aprendizaje adicionales requeridos

Buenas respuestas de todos.Me gustaría añadir un par de formas para evitar el lado oscuro de TDD:

  • He escrito apps para hacer su propio estudio aleatorizado de auto-prueba.El problema con la escritura de pruebas específicas es incluso si usted escribe un montón de ellos que sólo cubren los casos en los que piensas.Al azar de la prueba de generadores de encontrar problemas que no pensar.

  • El concepto entero de un montón de pruebas de unidad implica que usted tiene componentes que pueden entrar en estados no válidos, como las estructuras de datos complejas.Si usted manténgase alejado de las estructuras de datos complejas hay mucho menos para probar.

  • En la medida en que su aplicación permite, ser tímido de diseño que se basa en la correcta ordenación de las notificaciones, eventos y efectos secundarios.Los puede conseguir fácilmente caído o revueltos, así que necesitan un montón de pruebas.

TDD requiere una cierta organización de su código.Esto podría ser ineficiente o difícil de leer.O incluso arquitectónicamente mal;por ejemplo, desde private los métodos no pueden ser llamadas fuera de una clase, tiene que hacer que los métodos no-privado para hacerlos comprobables, lo que está mal.

Cuando los cambios en el código, tienes que cambiar las pruebas.Con refactorización esta puede ser una gran cantidad de trabajo extra.

Permítanme añadir que si se aplica BDD los principios de TDD proyecto, puede aliviar algunos de los principales inconvenientes mencionados aquí (confusión, malentendidos, etc.).Si usted no está familiarizado con BDD, usted debe leer Dan al Norte de la introducción.Se le ocurrió el concepto en respuesta a algunas de las cuestiones que surgieron a partir de la aplicación de TDD en el lugar de trabajo.Dan introducción a la BDD se puede encontrar aquí.

Yo sólo hacer esta sugerencia porque BDD direcciones de algunos de estos aspectos negativos y actúa como una brecha de parada.Usted querrá considerar esto al recoger su feedback.

Usted tiene que asegurarse de que sus pruebas son siempre hasta la fecha, el momento de empezar a ignorar las luces rojas es el momento en que las pruebas se vuelven sin sentido.

Usted también tiene que asegurarse de que las pruebas son integrales, o el momento en que un gran error aparece, la congestión tipo de administración que finalmente convenció para hacerle pasar el tiempo de escribir más código que se quejan.

La persona que enseñó a mi equipo de desarrollo ágil a pesar de no creer en la planificación, sólo escribió para el más mínimo requisito.

Su lema era refactorizar, refactorizar, refactorizar.Llegué a entender que refactorizar, que significa "no planificar con anticipación'.

El tiempo de desarrollo aumenta :Cada método de necesidades de pruebas, y si usted tiene una gran aplicación con las dependencias que necesita para preparar y limpiar los datos para las pruebas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top