desymfonyday 2014 - to mock or not to mock - spanish
DESCRIPTION
TRANSCRIPT
![Page 1: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/1.jpg)
![Page 2: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/2.jpg)
![Page 3: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/3.jpg)
¿Quiénes somos?
Eloi Poch Jordi Llonch
![Page 4: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/4.jpg)
Agenda• ¿Qué hacemos en Akamon?
• To test or not to test?
• ¿Cómo hacemos test?
• Librerías de mock.
• Conclusiones y consejos.
![Page 5: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/5.jpg)
• Osiris:
• Nueva plataforma de servicios de Akamon.
• Principios:
• Simplicidad sobre facilidad.
¿Qué hacemos?
Calidad
Funcionalidad Velocidad
![Page 6: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/6.jpg)
Domain
Con
tract
Infrastructure
Domain (Business Logic)
Con
tract
Infrastructure
Domain (Business Logic)
Applications
Background
Website
APIsAPIsAPIs
Arquitecturaby Akamon
APIs
DBs
Con
tract
External Services
Infrastructure
Domain (Business Logic)
![Page 7: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/7.jpg)
Calidad
• Pull Request • Pair Programming • Test
by Akamon
![Page 8: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/8.jpg)
To test or not to test?
Todo e
l mund
o
hace t
ests
![Page 9: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/9.jpg)
To test or not to test
Los buenos test ahorran DINERO en cualquier aplicación
![Page 10: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/10.jpg)
To test or not to test
![Page 11: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/11.jpg)
Testear es controvertido
• David Heinemeier (creador de Ruby on Rails y Fundador y CTO de Basecamp)
• TDD is dead. Long live testing & RailsConf 2014 Keynote - Writing Software by DHH
• Hangouts entre Kent Beck, DHH y M. Fowler
El caso DHH
![Page 12: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/12.jpg)
¿Cómo hacemos test?
Akamon
Way
![Page 13: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/13.jpg)
Un feedback rápido…
…para el código que estamos escribiendo…
…de una funcionalidad.
¿Qué queremos de los tests?
![Page 14: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/14.jpg)
Testean de manera rápida una funcionalidad.
¿Qué hacen nuestros tests?
![Page 15: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/15.jpg)
Infrastructure
External Services
Test Double
Test DoubleAPIs
DBs
Con
tract
Domain (Business Logic)
![Page 16: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/16.jpg)
Test doubles• Implementación alternativa de un colaborador
(clase o servicio)
• Objetivo:
• Hacer nuestros tests rápidos.
• Aislar sistemas externos.
• Testear casos extremos.
![Page 17: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/17.jpg)
Test doubles• Tipos:
• Dummy: Implementación no funcional.
• Fake: Implementación completamente funcional pero no usable en un entorno real.
• Stub: Implementación parcialmente funcional preparada únicamente para poder ser usada en el test.
• Spy: Stub con memoria.
• Mock: Spy con expectaciones sobre las llamadas a recibir que las auto-comprueba él mismo.
![Page 18: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/18.jpg)
Tests UnitariosPros Contras
Rápidos No pueden probar la ausencia de errores
Permiten testear todos los casos Los doubles no son confiables
Ayudan a encontrar problemas pronto
Los doubles están acoplados a sus implementaciones
Facilitan el cambio
Buena documentación
![Page 19: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/19.jpg)
Escuelas de Tests Unitarios
Chicago / Classical London / Mockist
Énfasis Algoritmo Envío de mensajes
Enfoque Resultado, estado y efectos colaterales
Roles, responsabilidades e
interacciones
LibroTest-Driven
Development by Example
Growing Object Oriented Software Guided by Tests
![Page 20: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/20.jpg)
Unit Testing :: Chicago School• Pros:
• Menor riesgo de expectaciones incorrectas.
• Tests estables.
• Menos mantenimiento.
• Contras:
• Ciclo largo de Red-Green-Refactor.
• Explosión combinatoria de los casos a testear.
• Un simple bug rompe muchos tests.
• Más debugging para encontrar la raíz del error.
![Page 21: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/21.jpg)
Unit Testing :: London School• Pros:
• Facilita el enfoque.
• Baby steps.
• Contras:
• Alto riesgo de expectaciones incorrectas (el refactoring no detecta cambios en los colaboradores)
• Acople del test a la implementación.
• Test frágiles.
![Page 22: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/22.jpg)
Glosario• Test Unitarios:!
• Test del código y la lógica de negocio de una funcionalidad (operaciones con el contrato del módulo).
• Test double: La infraestructura y los servicios externos.
• Riesgo:
• Test demasiado profundo que no permita testear de manera simple todas las casuísticas
by Akamon
![Page 23: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/23.jpg)
…pero los test unitarios no son suficientes…
![Page 24: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/24.jpg)
Queremos saber si las integraciones con los sistemas
externos funcionan correctamente.
![Page 25: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/25.jpg)
Infrastructure
External ServicesAPIs
DBs
Con
tract
Domain (Business Logic)
![Page 26: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/26.jpg)
Tests de Integración
Pros Contras
Confiables Lentos
Encontrar problemas/cambios con servicios de terceros
Imposible de testear algunos casos extremos
Buena documentación
![Page 27: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/27.jpg)
• Test de integración:!
• Test de las implementaciones de los contratos/interfaces de la infraestructura y los servicios externos.
• Test double: Nada.
by AkamonGlosario
![Page 28: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/28.jpg)
…pero con test unitario e integración todavía no es
suficiente…
![Page 29: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/29.jpg)
Applications
APIs
Background
Website
APIsAPIs ?Domain
Module User
Module Analytics
Module Marketing
Module Device
Module Economy
Module Game
Module Game Event
![Page 30: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/30.jpg)
Tests de AceptaciónPros Contras
Amigables y entendibles para usuarios no técnicos Lentos
Gran confianza Complicado de testear algunos casos
Buena documentación Imposible testear algunos casos extremos
Complicado localizar errores
Complicados de escribir (estado inicial)
![Page 31: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/31.jpg)
Glosario
• Test de aceptación:!
• Test de una funcionalidad (end-to-end black-box mode).
• Test double: Nada.
by Akamon
![Page 32: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/32.jpg)
Nuestra pirámide de tests
Acceptance
Integration
Unit
![Page 33: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/33.jpg)
Librerías mock
https://github.com/Akamon/to-mock-or-not-to-mock
![Page 34: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/34.jpg)
PHPUnit_MockObject/** @test */!public function it_should_publish_a_message()!{! $body = 'my message';! $message = new Message($body);! $serializedMessage = ['body' => $body];!! $serializer = $this->getMock(SerializerInterface::class, ['serialize']);! $serializer! ->expects($this->once())! ->method('serialize')! ->with($this->identicalTo($message))! ->will($this->returnValue($serializedMessage));!! $sender = $this->getMock(SenderInterface::class, ['send']);! $sender! ->expects($this->once())! ->method('send')! ->with($this->equalTo($serializedMessage))! ->will($this->returnValue(true));!! $publisher = new Publisher($serializer, $sender);! $this->assertTrue($publisher->send($message));!}
![Page 35: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/35.jpg)
Mockery/** @test */!public function it_should_publish_a_message()!{! $body = 'my message';! $message = new Message($body);! $serializedMessage = ['body' => $body];!! $serializer = Mockery::mock(SerializerInterface::class);! $serializer! ->shouldReceive('serialize')! ->with(Mockery::mustBe($message))! ->once()! ->andReturn($serializedMessage);!! $sender = Mockery::mock(SenderInterface::class);! $sender! ->shouldReceive('send')! ->with($serializedMessage)! ->once()! ->andReturn(true);!! $publisher = new Publisher($serializer, $sender);! $this->assertTrue($publisher->send($message));!}
![Page 36: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/36.jpg)
Phake/** @test */!public function it_should_publish_a_message()!{! $body = 'my message';! $message = new Message($body);! $serializedMessage = ['body' => $body];!! $serializer = Phake::mock(SerializerInterface::class);! Phake::when($serializer)! ->serialize($message)! ->thenReturn($serializedMessage);!! $sender = Phake::mock(SenderInterface::class);! Phake::when($sender)! ->send($serializedMessage)! ->thenReturn(true);!! $publisher = new Publisher($serializer, $sender);! $this->assertTrue($publisher->send($message));!! Phake::verify($serializer, Phake::times(1))->serialize($message);! Phake::verify($sender, Phake::times(1))->send($serializedMessage);!}
![Page 37: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/37.jpg)
Prophecy/** @var Prophet */!private $prophet;!!protected function setup()!{! $this->prophet = new Prophet();!}!!protected function tearDown()!{! $this->prophet->checkPredictions();!}!!/** @test */!public function it_should_publish_a_message()!{! $body = 'my message';! $message = new Message($body);! $serializedMessage = ['body' => $body];!! $serializer = $this->prophet->prophesize(SerializerInterface::class);! $serializer! ->serialize($message)! ->shouldBeCalled()! ->willReturn($serializedMessage);!! $sender = $this->prophet->prophesize(SenderInterface::class);! $sender! ->send($serializedMessage)! ->shouldBeCalled()! ->willReturn(true);!! $publisher = new Publisher($serializer->reveal(), $sender->reveal());! $this->assertTrue($publisher->send($message));!}
![Page 38: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/38.jpg)
ComparativaPHPUnit 4.1 Mockery 0.9 Phake 1.1 Prophecy 1.0 Notas
Invocation Count Constraint Ok Muy bueno Muy bueno Ok
Mockery/Phake son mejores. Métodos atMost() y atLeast()
disponibles.
Ordered Expectations Ok Bueno Ok No Mockery es mejor. Simplemente usando ordered([group]).
Argument Matchers Bueno Ok Ok Ok PHPUnit es mejor. Sobre todo con la funcionalidad delta
Partial Mocking Ok Muy bueno Ok No La construcción es mucho más simple con Mockery.
Mocking Demeter Chains And Fluent
InterfacesOk Muy bueno Ok Ok Muy sencillo con Mockery y un poco
rebuscado con los otros.
Test Doubles Ok Bueno Ok BuenoProphecy es el único con spies y Mockery el único que gestiona
static, final & private
![Page 39: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/39.jpg)
Otro punto de vista
![Page 40: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/40.jpg)
Property-Based Testing• Aproximación totalmente diferente.
• No se escriben los tests, se generan.
• QuickCheck: Canonical framework escrito en Haskell.
• ¿Cómo funciona?:
• Describe las entradas y salidas permitidas y las transiciones de estado.
• Genera aleatóriamente un gran número de casos de test y busca fallos.
• Devuelve el mínimo ejemplo de fallo.
• Casos de uso: Comportamientos no determinísticos y sistemas concurrentes.
![Page 41: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/41.jpg)
Conclusiones
En el mundo del testing lo más importante es determinar la unidad o sistema a testear.
Los tipos de tests, los doubles e incluso los frameworks dependen de la unidad o sistema a
testear.
![Page 42: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/42.jpg)
Consejos
• Evita Minimiza los dobles:
• Están acoplados a la implementación ➙ Limitan la refactorización pura.
• No son confiables ➙ Pueden tener un comportamiento diferente al real.
by Akamon
![Page 43: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/43.jpg)
Consejos
• Los test unitarios testean código no funcionalidad:
• TDD por si solo no es suficiente.
• BDD por si solo puede ser suficiente (si no te importa la lentitud).
• TDD + BDD = WIN
by Akamon
![Page 44: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/44.jpg)
Consejos
• Separa la lógica del test de los datos del test:
• Usa el @dataProvider de PHPUnit (no para los doubles).
• Usa el Scenario Outline de Behat.
by Akamon
![Page 45: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/45.jpg)
• La unidad a testear:
• Mockists are dead. Long live classicists.
• Unit Tests: Isolation
• Mock aren’t stubs: Dummy, Fake, Stub and Mock
• TTDD (Tautological TDD): An anti-pattern
• The depth of tests
• Avoid Testing Implementation Details, Test Behaviours
• The Failures of "Intro to TDD"
• The London School of Test Driven Development
Referencias
![Page 46: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/46.jpg)
• Test doubles:
• Tests doubles
• Mocks, fakes, stubs and dummies
• The little mocker
Referencias
![Page 47: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/47.jpg)
• Contract tests (a.k.a integration tests):
• Integrated tests are scam (Collaboration & Contract tests)
• Integration contract tests
Referencias
![Page 48: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/48.jpg)
• La pirámide de los tests:
• Test Pyramid
• Testing Pyramid: A case study
• Inverting the testing pyramid
• Testing ice-crean corn anti-pattern
Referencias
![Page 49: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/49.jpg)
Referencias
• Property-Based testing:
• Better than unit tests
• Powerful testing with test.check
• Testing the Hard Stuff and Staying Sane
![Page 50: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/50.jpg)
• Entrevistas a veteranos del TDD:
• Ron Jeffries (One of the founders of Extreme Programming & Agile Manifesto)
• Steve Freeman (Co-author of Growing Object-Oriented Software Guided by Tests)
• James Shore (Author of The Art of Agile Development)
• J.B. Rainsberger (Author of JUnit Recipes : Practical Methods for Programmer Testing
Referencias
![Page 51: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/51.jpg)
¿Preguntas?
¡Gracias!
![Page 52: DeSymfonyDay 2014 - To mock or not to mock - Spanish](https://reader034.vdocumento.com/reader034/viewer/2022051610/548c84f7b479594e508b49d0/html5/thumbnails/52.jpg)
Imágeneshttp://transmedialshakespeare.files.wordpress.com/2011/01/3459513455_d1288a14b9_o.jpeg
http://info.fonality.com/Portals/65551/images/Presentation.jpg
http://www.renders-graphiques.fr/image/upload/normal/organisateur_bloc_notes_agenda-.png
http://www.luxfacta.com/wp-content/uploads/2014/01/programar.jpg
http://www.kinderlandshop.es/WebRoot/StoreES2/Shops/eb0950/5020/E112/E380/66B2/C9CA/AC10/1414/0340/C05_1.jpg
http://www.allpurposeabrasives.com.au/uploads/images/Quality%20Assurrance.jpg
http://news.liv.ac.uk/wp-content/uploads/2013/03/knowledgeHOMEb.jpg
http://www.worldnpa.org/site/wp-content/uploads/2013/01/hand-writing.jpg
http://ievasapple.com/photo/images/my%20point%20of%20view.JPG
http://www.snegmortgageteam.ca/wp-content/uploads/2013/12/mortgage-glossary.jpg
http://birddogrealestate.net/wp-content/uploads/2012/09/Home-Toolbox.jpg
http://www.balticblues-events.com/sites/default/files/images/ideas/photos/32-Live-Cooking1.jpg
http://www.youwall.com/papel/on_my_way_wallpaper_fa2bb.jpg
http://inheritancethefilm.com/wp-content/uploads/2013/06/thankyou.jpg
http://www.ngn.com.au/wp-content/uploads/2013/07/Websites-on-white.png
http://hn-marketing.co.uk/wp-content/uploads/2012/09/shutterstock_12553684.jpg
http://picturesofmoney.org/wp-content/uploads/2013/04/Animated-American-Money-Falling-Into-a-Pile.jpg
http://st.gdefon.com/wallpapers_original/wallpapers/18716_palma_more_plyazh_pesok_oblaka_tropiki_2560x1600_(www.GdeFon.ru).jpg
http://i.imgur.com/DbTGPys.gif
http://media.tumblr.com/e96b538708be71104f21689d3a820b9c/tumblr_inline_n54iazp56x1raprkq.gif
http://i.imgur.com/8Lpsys5.gif