<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marco Ramírez</title>
    <description>The latest articles on DEV Community by Marco Ramírez (@rzerostern).</description>
    <link>https://dev.to/rzerostern</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F407867%2F0c9a23a5-4c77-49ab-9dbd-510b2a6cb7dd.jpeg</url>
      <title>DEV Community: Marco Ramírez</title>
      <link>https://dev.to/rzerostern</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rzerostern"/>
    <language>en</language>
    <item>
      <title>Pruebas Unitarias en Laravel con Sanctum</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Sat, 07 Sep 2024 00:11:49 +0000</pubDate>
      <link>https://dev.to/rzerostern/pruebas-unitarias-en-laravel-con-sanctum-4556</link>
      <guid>https://dev.to/rzerostern/pruebas-unitarias-en-laravel-con-sanctum-4556</guid>
      <description>&lt;p&gt;Qué hay, mis niños, espero que estén pasándola de maravilla y que estén teniendo una gran semana, e incluso un mejor mes. Este post lo escribí dentro de &lt;a href="https://thedevgang.com/2024/09/04/pruebas-unitarias-en-laravel-con-sanctum/" rel="noopener noreferrer"&gt;thedevgang.com&lt;/a&gt; y lo comparto por acá para que tenga más engagement con todos ustedes. Espero que les guste :3&lt;/p&gt;

&lt;p&gt;Ya es el último jalón del 2024 y de otras cosas más, de las cuales no vale la pena platicar en este momento. Pues bien, en una publicación anterior del blog hicimos la migración de la librería de autenticación Passport a Sanctum, sin embargo, ahora, me gustaría ahondar en las pruebas unitarias de algunos endpoints y así poder ejecutarlas en algún pipeline de integración contínua como Github Actions.&lt;/p&gt;

&lt;p&gt;Previamente, había escrito sobre cómo hacer pruebas unitarias con Passport en dev.to, este post lo puedes encontrar aquí , en donde expongo también qué son las pruebas unitarias y aspectos básicos sobre su implementación en Laravel. En este post, abarcaremos lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pruebas unitarias ya con Sanctum implementado&lt;/li&gt;
&lt;li&gt;Probando algunos endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pruebas unitarias con Sanctum implementado
&lt;/h2&gt;

&lt;p&gt;Para el caso de este post, tengo algunos endpoints que armé para un proyecto alterno que he estado desarrollando desde hace unos meses. Este proyecto tiene las siguientes características en cuanto al framework y demás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Laravel 11 con Sanctum 4&lt;/li&gt;
&lt;li&gt;PHPUnit 10&lt;/li&gt;
&lt;li&gt;Laravel Sail como ambiente de desarrollo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Probaremos en este caso tres endpoints que armamos para el proceso de autenticación de esta app, primero haremos lo pertinente con el siguiente método:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'device_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fails&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;badRequestStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'device_id'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;successStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Unauthorized'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unauthorizedStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este método es el que gestiona por completo el proceso de login de nuestra app, sin embargo el registro no se inclute en este snippet, ese será el próximo a probar. En este caso, lo hemos confirmado y al parecer funciona correctamente, pero para poder cerciorarnos de ello, armaremos sus respectivas pruebas.&lt;/p&gt;

&lt;p&gt;Primeramente con terminal ingresa este comando:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php artisan make:test  UserTest --unit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Esto te creará un archivo UserTest en la carpeta tests/Unit, el cual estará completamente “en blanco”, como el siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Tests\Unit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;PHPUnit\Framework\TestCase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * A basic test example.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_basic_test&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Borra el método test_basic_test(), no lo necesitaremos. En este caso digo que está en blanco porque es únicamente el mock de nuestras pruebas unitarias y para esta ocasión será el que usemos para los métodos antes mencionados. Ahora bien, antes de comenzar a programar las pruebas, necesitamos asegurarnos de los casos de uso que estaremos ejecutando y probando, por lo tanto tenemos los siguientes casos de uso a probar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login correcto.&lt;/li&gt;
&lt;li&gt;Login inválido ingresando todos los datos.&lt;/li&gt;
&lt;li&gt;Registro correcto.&lt;/li&gt;
&lt;li&gt;Registro del perfil correcto.&lt;/li&gt;
&lt;li&gt;Registro del perfil erróneo por no ingresar datos.&lt;/li&gt;
&lt;li&gt;Perfil no encontrado.&lt;/li&gt;
&lt;li&gt;Registro del perfil correcto y su retroalimentación.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Una vez enumerados los casos de uso, tenemos en cuenta de que los que cubren en este caso el método antes mencionado son los casos 1 y 2, por lo que procederemos con ellos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparación de las pruebas
&lt;/h2&gt;

&lt;p&gt;Ahora bien, antes de comenzar a codificar las pruebas, necesitamos configurar las mismas para que puedan ser ejecutadas correctamente, para ello crearemos dentro del archivo UserTest el método setUp, el cual realiza la ejecución de instrucciones previo a ejecutar las pruebas unitarias. Aquí es donde nosotros podemos indicarle al sistema que debe realizar las migraciones y poder comenzar con las mismas en caso de requerir datos, así como de asignación de valores en variables. El método setUp que crearemos está estructurado de esta manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\Faker\Factory&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'valid@test.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;deviceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'migrate:fresh'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-vvv'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El setUp hará lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crea una instancia de Faker, una librería para simular ingreso de datos de diversos tipos de variables.&lt;/li&gt;
&lt;li&gt;Creamos un nombre ficticio&lt;/li&gt;
&lt;li&gt;Asignamos el password y el correo electrónico a valores predeterminados.&lt;/li&gt;
&lt;li&gt;Asignamos un ID de dispositivo ficticio igualmente con el faker.&lt;/li&gt;
&lt;li&gt;Correrá las migraciones de la base de datos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Arriba de este método, declara las variables globales que usaremos para todas nuestras pruebas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$faker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$deviceId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Desarrollo de las pruebas unitarias
&lt;/h2&gt;

&lt;p&gt;Para la prueba 1, necesitamos asegurarnos que el login es correcto invocando el endpoint al que llamaremos en nuestra app. Crearemos el método &lt;code&gt;test_login_success&lt;/code&gt; y quedaría de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_login_success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-vvv'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'device_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;deviceId&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/api/login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Accept'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este método, primeramente alimentará la base de datos con los catálogos pertinentes para poder confirmar que los mismos existen sin problemas. Después asignará el body y enviará los datos por medio de un request POST, al enviarlo, revisará que el status que devuelva su llamada es 200 y que los datos sean conforme al arreglo solicitado para confirmar, en este caso [ “success” =&amp;gt; true ]. Si todo sale bien y se cumplen las condiciones, se considera prueba satisfactoria, en caso contrario, se considerará fallida y es donde se tendrá que revisar nuevamente el código.&lt;/p&gt;

&lt;p&gt;Ahora bien, haremos el caso de uso 2. Para ello crea un método llamado &lt;code&gt;test_login_error_with_data_ok&lt;/code&gt; e ingresa el siguiente código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_login_error_with_data_ok&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-vvv'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'invalid@test.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'device_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;deviceId&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/api/login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A diferencia del anterior, en este caso, se le entregan datos erróneos y se solicita que confirme que el endpoint devuelva un error 401, así como un body [“success” =&amp;gt; false ], esto con el fin de que se confirme que el sistema deniega el acceso a alguien que no tenga credenciales correctas.&lt;/p&gt;

&lt;p&gt;Con esto, cubrimos el método presentado anteriormente y ya quedaría cubierto el método. Para poder probarlo, podemos ejecutar el siguiente comando bajo Sail:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker compose exec laravel.test php artisan test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Te mostrará los siguientes resultados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  Tests\Unit\UserTest
  ✓ login error with data ok 0.08s  
  ✓ login success 0.16s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si te sale todo bien como te lo he mostrado, tus unit tests han salido satisfactoriamente, pero estamos lejos de terminar. Ahora necesitamos probar el siguiente método:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|email|unique:users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'c_password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|same:password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'device_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fails&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;badRequestStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;successStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Unauthorized'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;unauthorizedStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este caso, realizaremos el caso de uso 3, el cual solicita confirmar que el registro sea correcto, para ello, crea el método &lt;code&gt;test_register_success&lt;/code&gt; e ingresa el siguiente código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_register_success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'c_password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'device_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;deviceId&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'/api/register'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al igual que con el login, solicitamos que nos confirme el sistema que se nos está entregando un código 200 así como el arreglo &lt;code&gt;[“success” =&amp;gt; true]&lt;/code&gt;, si logramos eso, ya hemos terminado, pero si te das cuenta, nos hace falta la prueba en caso de que se equivoque el usuario. Ese método te lo dejo de tarea para que puedas corroborar tus conocimientos.&lt;/p&gt;

&lt;p&gt;Ahora bien probaremos los siguientes métodos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;successStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Usuario no encontrado.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;notFoundStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'first_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'last_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'birth_date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'bloodtype'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'phone'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'gender'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'country'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'state'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|numeric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fails&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;badRequestStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nv"&gt;$profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="nv"&gt;$dataInsert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dataInsert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dataInsert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;


            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Perfil actualizado correctamente.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;successStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;QueryException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Error al actualizar el perfil.'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;internalServerErrorStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este par de métodos son los referentes a la gestión del perfil del usuario y su retroalimentación, por lo que los casos de uso que debemos probar son del 4 al 7. Para el caso 4, debemos crear un nuevo método llamado &lt;code&gt;test_register_profile_success&lt;/code&gt; y agregamos el siguiente código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_register_profile_success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'first_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'last_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'birth_date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1987-10-10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'bloodtype'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'phone'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'gender'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'country'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'state'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TestToken'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;plainTextToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/user/profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En esta ocasión, necesitamos declarar un arreglo que simule el contenido del cuerpo del request para que pueda ser enviado correctamente por el endpoint y una vez enviado, el confirmar que el request tiene una respuesta satisfactoria (200).&lt;/p&gt;

&lt;p&gt;Para el caso del perfil erróneo por no ingresar datos, necesitamos agregar un nuevo método que denominaremos &lt;code&gt;test_register_profile_validation_failed&lt;/code&gt;, el cual implementaremos de la siguiente forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_register_profile_validation_failed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TestToken'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;plainTextToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/user/profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este caso, es prácticamente el mismo contenido de la prueba anterior, con la diferencia que ahora le enviamos un arreglo en blanco, para poder asegurarnos que si no se están enviando los datos correctamente, no permita la creación del perfil del usuario por medio de un Bad Request error (400).&lt;/p&gt;

&lt;p&gt;El siguiente método probará que en caso de no encontrar el perfil de algún usuario, así lo indique con un código 404, por lo que creamos otro método denominado &lt;code&gt;test_obtain_profile_not_found&lt;/code&gt; e ingresando el siguiente código.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_obtain_profile_not_found&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TestToken'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;plainTextToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/user/profile'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el modelo de negocio, nosotros al registrarnos, creamos el usuario, mas no el perfil que tiene que ser ingresado posteriormente, por lo que al momento de ejecutar la prueba unitaria, al ejecutar el request para obtener el perfil, nos enviará un código 404, comportamiento que estamos buscando para esta prueba unitaria.&lt;/p&gt;

&lt;p&gt;Finalmente para el último caso de uso, crearemos el método &lt;code&gt;test_register_profile_and_obtain&lt;/code&gt; para confirmar que un mismo test pueda obtener dos comportamientos en un mismo flujo. Para este caso implementaremos el siguiente código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_register_profile_and_obtain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'first_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'last_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'birth_date'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1987-10-10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'bloodtype'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'phone'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'gender'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'country'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'state'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TestToken'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;plainTextToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/user/profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeaders&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/user/profile'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este test, implementamos dos casos de uso realizados previamente, el primero es la creación del perfil y posteriormente, retroalimentamos el perfil, indicando a PHPUnit que deseamos confirmar que el response del endpoint que retroalimenta el perfil sea satisfactoria (código 200). Igualmente podríamos realizar el assert de la inserción de datos cambiando algunas líneas de código, pero por el momento es más que suficiente.&lt;/p&gt;

&lt;p&gt;Ya terminando las pruebas unitarias, procedemos a ejecutar el comando docker compose exec laravel.test php artisan test y confirmamos el estatus de nuestras pruebas unitarias. Si nos salen de esta forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  Tests\Unit\UserTest
  ✓ login error with data ok.                 0.10s  
  ✓ login success.                            0.15s  
  ✓ register success.                         0.20s  
  ✓ register profile success.                 0.10s  
  ✓ register profile validation failed.       0.09s  
  ✓ obtain profile not found.                 0.10s  
  ✓ register profile and obtain.              0.10s  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Las pruebas unitarias salieron satisfactorias. En caso contrario, checa lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El método que haya tenido broncas, checa que no sea una situación de código.&lt;/li&gt;
&lt;li&gt;Checa que la configuración de PHPUnit sea la adecuada, ahondaremos en ello en el siguiente post.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Igualmente, voy a explicarles cómo configurar el Github Actions para ejecutar las pruebas unitarias en este y poder inclusive obtener reportes de cobertura de código y un  posible despliegue contínuo. Espero que este post, aunque largo, sirva para que tengan más contexto sobre pruebas unitarias y sobre un proceso de integración y despliegue continuos.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>spanish</category>
      <category>backend</category>
    </item>
    <item>
      <title>Laravel: Migrar de Passport a Sanctum</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Wed, 14 Aug 2024 06:10:45 +0000</pubDate>
      <link>https://dev.to/rzerostern/laravel-migrar-de-passport-a-sanctum-2n7b</link>
      <guid>https://dev.to/rzerostern/laravel-migrar-de-passport-a-sanctum-2n7b</guid>
      <description>&lt;p&gt;Mis chamacos ¿me extrañaron?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jTHCf8E5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/I_bommKQQWEAAAAd/tenor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jTHCf8E5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://c.tenor.com/I_bommKQQWEAAAAd/tenor.gif" width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Espero que no, amigos. Estuve muy alejado por cuestiones laborales de este bonito espacio, pero a la vez quisiera comentarles que he abierto un espacio dedicado a todos los apuntes, tutoriales, guías y demás posts que vaya armando aquí o en él. &lt;/p&gt;

&lt;p&gt;Su nombre es &lt;strong&gt;The Dev Gang&lt;/strong&gt; y tiene como objetivo el conglomerar ahí todo el conocimiento que les traigo en español y ¿por qué no? en un futuro que haya más colaboradores (algunos dirán ¡NO MAMES, STERNIE!, quédate aquí), pero pues la verdad es que lo hago para probar AdSense y ver si aunque sea cae para los cigarros con ello. Pueden verlo desde &lt;a href="https://thedevgang.mx" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dicho lo anterior, vamos a darle con este bonito post.&lt;/p&gt;

&lt;p&gt;Verán, al trabajar en este caso con Laravel, nos encontramos en ocasiones que deseamos tener el clásico comportamiento de autenticación de nuestras REST APIs, por lo que para ello tenemos dos alternativas: Passport y Sanctum. Passport es una librería que provee de un servidor OAUTH2 para poder realizar este trabajo. Passport hace una implementación del mismo en minutos y con la que podemos contar con tokens JWT para poder acceder a endpoints restringidos en nuestra API.&lt;/p&gt;

&lt;p&gt;Ahora bien, Laravel, desde la versión 7, integró una nueva librería para autenticación más liviana que Passport y que a la vez puede generar múltiples tokens para los diferentes dispositivos que acceden a la API. Ahora bien, eso no quiere decir que Passport no lo haga, sin embargo, la pregunta del millón de dólares sería la siguiente:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Tu proceso de negocio requiere OAUTH2?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeHVhMjZkMWtjenhic3NydXlscmU2aGJ2ZXlqeDR5em9oeWg3OTRxZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/yoJC2Lkvb3FHlhifM4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeHVhMjZkMWtjenhic3NydXlscmU2aGJ2ZXlqeDR5em9oeWg3OTRxZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/yoJC2Lkvb3FHlhifM4/giphy.gif" width="500" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si tu respuesta es sí, usa Passport, si no, puedes decantarte por Sanctum, que en mi opinión es más económico, en cuanto a uso de recursos del sistema se refiere y a que en este caso, las tokens generadas por Sanctum son más duraderas (hasta años) y pueden ser revocadas manualmente. La desventaja que le veo a Sanctum es que como tal, no puedes renovar token alguna, pero esa no es como tal la intención de Sanctum, misma que sí es de Passport el refrescar una token. &lt;/p&gt;

&lt;p&gt;Si estás en el caso de uso de que no necesitas OAUTH2 o simplemente quieres cambiarte a Sanctum proveniente de Passport, entonces este post es para ti, te contaré el cómo migrarte de Passport a Sanctum en poco tiempo y poder así tener un proceso más eficiente en cuanto a autenticación se refiere.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ATENCIÓN: Migrar de Passport a Sanctum es un proceso que debes pensar muy bien y más si es que tienes muchísimos usuarios registrados en tu aplicación, puesto que el realizar dicha migración involucraría, en el caso más extremo, el solicitar a todos y cada uno de los usuarios registrados que vuelvan a ingresar una nueva contraseña, así que como decimos en México “tantéale el agua a los camotes”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pasos para migrar
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdDE5NXo0am5yaG0wbndiOWpzYmNrOTZlYXJheDdybTNtd2FydTdwZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3BKf0I2PVxAfC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdDE5NXo0am5yaG0wbndiOWpzYmNrOTZlYXJheDdybTNtd2FydTdwZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3BKf0I2PVxAfC/giphy.gif" width="200" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primeramente, debemos instalar Sanctum, esto lo hacemos con la siguiente línea:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require laravel/sanctum&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Posterior a eso, ejecuta la siguiente línea en tu terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Esto publicará los archivos de configuración de Sanctum a nuestra carpeta config. Esto incluye archivos de migración para la base de datos por lo que necesitas ejecutar:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php artisan migrate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Una vez realizada la instalación, es momento de manipular lo siguiente:&lt;/p&gt;

&lt;p&gt;Ve al archivo config/auth.php y cambia la guard que desees tener bajo Sanctum, por lo general es la de API la que queremos tener tokenizada, por lo que tendrás:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'api'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'passport'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'provider'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'hash'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cambia &lt;strong&gt;passport&lt;/strong&gt; por &lt;strong&gt;sanctum&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ahora, pasa hacia la clase User, esta se encuentra dentro de tu carpeta app/models. Busca esta línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Laravel\Passport\HasApiTokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;y cámbiala por esta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Laravel\Sanctum\HasApiTokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto bastará en la parte de nuestro modelo, pero es hora de meternos al servicio donde generamos el login y el registro. Comencemos con el login. Si tienes un método como el siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAuthAndRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$oClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password_client'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'grant_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'client_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'client_secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'scope'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'oauth/token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entonces tenemos que hacer un refactor. Primero cambia de getAuthAndRefreshToken a getToken, posteriormente cambia los parámetros que recibirás a $email, $password y $deviceID. El código en su interior se va, quedando primeramente así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deviceID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ingresa esta línea de código dentro del método:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto nos buscará el usuario con el correo electrónico que el usuario nos solicita. Si encuentra el usuario, proseguimos, en caso contrario, se regresa una respuesta de tipo 401 o 403 para denegar el acceso. Esto a la vez lo hacemos con este condicional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nc"&gt;Hash&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Correo electrónico o contraseña incorrectas'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Además de checar de que el usuario que buscamos existe, confirmamos que la contraseña es correcta entregando el password que nos da el usuario y confirmando que los hashes coincidan.&lt;/p&gt;

&lt;p&gt;En caso que la validación sea correcta, podemos regresar la token sin problemas de esta forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'access_token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$deviceID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;plainTextToken&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto bastará para el login y para el registro, ahora bien, en los métodos que utilices para el controlador del login y del registro, cuida de sustituir la línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTokenAndRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por esta otra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prueba ahora tu implementación, si te entrega un objeto similar al siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1|1QjmwWuwL3EocZcErFPw0RoBLYBT6mO9GNblOVVHd1f0ed6a"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La migración fue completada con éxito. Ahora es tiempo de hacer limpieza. Dentro de tu implementación, puedes eliminar lo siguiente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Las tablas que comienzan con OAUTH en la base de datos.&lt;/li&gt;
&lt;li&gt; Cualquier referencia a Passport que encuentres en tu código.&lt;/li&gt;
&lt;li&gt; Si hiciste la implementación de un middleware denominado &lt;code&gt;CheckClientCredentials&lt;/code&gt;, elimínalo.&lt;/li&gt;
&lt;li&gt; En el archivo &lt;code&gt;app/Http/Kernel.php&lt;/code&gt;, elimina esta línea (solo aplica si implementaste el CheckClientCredentials):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'CheckClientCredentials'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Http\Middleware\CheckClientCredentials&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Elimina del composer.json las líneas referentes a Passport.&lt;/li&gt;
&lt;li&gt; Ejecuta &lt;code&gt;composer update&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ya con esto, puedes implementar esta autenticación como de costumbre.&lt;/p&gt;

&lt;p&gt;Ya para terminar, debo mencionarte que las tokens generadas por Sanctum, se utilizan como tokens BEARER, por lo que estas las entregas a tu request, por medio de la cabecera Authorization de esta manera:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Authorization: Bearer 1|1QjmwWuwL3EocZcErFPw0RoBLYBT6mO9GNblOVVHd1f0ed6a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Para cada lenguaje y aplicación es diferente, por lo que dejo a tu criterio cómo la debes implementar en el frontend.&lt;/p&gt;

&lt;p&gt;Pues bien, espero que les haya gustado este post y procuraré tenerles muchos más posts de estos y ya no ausentarme. Por el momento estaré migrando el contenido a The Dev Gang, pero como les dije, por aquí seguiré escribiendo también.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>spanish</category>
      <category>backend</category>
    </item>
    <item>
      <title>Android: ¡Adios Groovy! Migrar a Kotlin nuestro build.gradle</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Tue, 30 May 2023 14:29:09 +0000</pubDate>
      <link>https://dev.to/rzerostern/android-adios-groovy-migrar-a-kotlin-nuestro-buildgradle-n12</link>
      <guid>https://dev.to/rzerostern/android-adios-groovy-migrar-a-kotlin-nuestro-buildgradle-n12</guid>
      <description>&lt;p&gt;Mis niños, hoy es un día un poco ajetreado, sin embargo me he caído de la cama y tengo energía para poder escribirles unas líneas ahora sobre un tema que nosotros hemos visto desde hace tiempo en el desarrollo de Android: migrar los archivos build.gradle a Kotlin.&lt;/p&gt;

&lt;p&gt;Recientemente, en la documentación de Google se ha notificado que Kotlin Script (KTS) será el lenguaje por default ahora para la sincronización de dependencias y que a partir de la siguiente versión de Android Studio, este proceso será por default en nuevas apps, por lo que este pequeño tutorial podrá servirte para poder migrar un pequeño proyecto de Groovy a KTS.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es KTS?
&lt;/h2&gt;

&lt;p&gt;KTS es el acrónimo para Kotlin Script, en sí el migrar de lenguaje entre Groovy y Kotlin no quiere decir que vamos a cambiar el núcleo de la importación de librerías en Android, que seguirá haciéndose con Gradle. Para los nuevos, Gradle es una herramienta de construcción de aplicaciones y automatización de rutinas compatible con diversos lenguajes de programación; esta fue construida con Java y fue el sustituto de Ant cuando llegó Android Kit Kat (o un poco antes). &lt;/p&gt;

&lt;p&gt;Hay varias diferencias entre archivos KT y archivos KTS, una de ellas es que los archivos KT son interpretados por el compilador de Kotlin, mientras que los KTS por el soporte de Scripting de Kotlin, por lo que no necesitan una compilación previa (¿Javascript eres tú?) y en este caso es como Gradle lo tomaría en cuenta sin necesidad de compilarlo previamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Tarda mucho?
&lt;/h2&gt;

&lt;p&gt;Pensarán muchos que esto es muy difícil y hasta tardado, pero en realidad es muy sencillo, pues solamente hay que cambiar cierta sintaxis dentro de los archivos build.gradle para conseguirlo, así que es hora de meter las manos al fuego&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Cambiar el settings.gradle
&lt;/h2&gt;

&lt;p&gt;Lo primero sería cambiar de nombre el archivo, agregándole la extensión &lt;strong&gt;.kts&lt;/strong&gt; al archivo y tendrás la siguiente línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="s1"&gt;':app'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En KTS, debes cambiarla por las siguientes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ya no hay que hacer más en este archivo e inclusive si quieres probarlo, te dejará sincronizar tus dependencias. Ahora bien, es importante que no uses comillas simples ('), pues Kotlin te las interpretará como caracteres y te marcará error. Procura siempre usar comillas (") en las cadenas de caracteres para que no tengas ningún problema.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Cambiar el build.gradle raíz
&lt;/h2&gt;

&lt;p&gt;Ahora bien, haz lo mismo de cambiar el nombre del archivo en el build.gradle raíz y cambia lo siguiente:&lt;/p&gt;

&lt;p&gt;Si tienes asignadas variables de versión (en un nodo ext) haz lo siguiente. Cambia lo siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;androidGradleVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"8.0.1"&lt;/span&gt;
    &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;androidKotlinPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.8.20"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;buildscript&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;androidGradlePlugin&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"8.0.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;androidKotlinPlugin&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.8.20"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aún no terminamos. Más abajo tendremos el nodo de &lt;strong&gt;plugins&lt;/strong&gt;, cambiémoslo de esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"com.android.application:$androidGradleVersion"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"com.android.library:$androidGradleVersion"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin.android:$androidKotlinPlugin"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a esto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.android.application"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"${extra["&lt;/span&gt;&lt;span class="n"&gt;androidGradlePlugin&lt;/span&gt;&lt;span class="s"&gt;"]}"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.android.library"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"${extra["&lt;/span&gt;&lt;span class="n"&gt;androidGradlePlugin&lt;/span&gt;&lt;span class="s"&gt;"]}"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin.android"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"${extra["&lt;/span&gt;&lt;span class="n"&gt;androidKotlinPlugin&lt;/span&gt;&lt;span class="s"&gt;"]}"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y ya tendremos listo este archivo, ahora es tiempo de pasar al archivo (o archivos) build.gradle de nuestros módulos.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Cambiar el build.gradle de nuestro módulo
&lt;/h2&gt;

&lt;p&gt;En este caso cambiaremos el del módulo &lt;strong&gt;app&lt;/strong&gt;, en este caso, si tienes más módulo, tendrás que hacer prácticamente lo mismo para el resto. Igual que los otros 2, cámbiale el nombre al archivo agregando extensión KTS al final. Luego cambia lo siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'com.android.application'&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'org.jetbrains.kotlin.android'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;por esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.android.application"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin.android"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Igualmente en la parte de la definición de Android, agrega a todas las definiciones un signo de igual (=) a cada una de ellas, es decir, en lugar de esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="s1"&gt;'mx.dev1.deadpool'&lt;/span&gt;
    &lt;span class="n"&gt;compileSdk&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;

    &lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"mx.dev1.deadpool"&lt;/span&gt;
        &lt;span class="n"&gt;minSdk&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
        &lt;span class="n"&gt;targetSdk&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;
        &lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;versionName&lt;/span&gt; &lt;span class="s2"&gt;"1.0"&lt;/span&gt;

        &lt;span class="n"&gt;testInstrumentationRunner&lt;/span&gt; &lt;span class="s2"&gt;"androidx.test.runner.AndroidJUnitRunner"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;buildTypes&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;release&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;minifyEnabled&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="n"&gt;proguardFiles&lt;/span&gt; &lt;span class="nf"&gt;getDefaultProguardFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'proguard-android-optimize.txt'&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'proguard-rules.pro'&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;compileOptions&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sourceCompatibility&lt;/span&gt; &lt;span class="n"&gt;JavaVersion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VERSION_1_8&lt;/span&gt;
        &lt;span class="n"&gt;targetCompatibility&lt;/span&gt; &lt;span class="n"&gt;JavaVersion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VERSION_1_8&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;kotlinOptions&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;jvmTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1.8'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;que quede esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;android&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mx.dev1.foo"&lt;/span&gt;
    &lt;span class="n"&gt;compileSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;

    &lt;span class="nf"&gt;defaultConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mx.dev1.foo"&lt;/span&gt;
        &lt;span class="n"&gt;minSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
        &lt;span class="n"&gt;targetSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;
        &lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;versionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;

        &lt;span class="n"&gt;testInstrumentationRunner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.test.runner.AndroidJUnitRunner"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;buildTypes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;release&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;isMinifyEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
            &lt;span class="nf"&gt;proguardFiles&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nf"&gt;getDefaultProguardFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"proguard-android-optimize.txt"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s"&gt;"proguard-rules.pro"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;compileOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sourceCompatibility&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JavaVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VERSION_1_8&lt;/span&gt;
        &lt;span class="n"&gt;targetCompatibility&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JavaVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VERSION_1_8&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;kotlinOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;jvmTarget&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.8"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente, encierra entre paréntesis los nombres de tus implementaciones para que queden en vez de esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.core:core-ktx:1.8.0'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.appcompat:appcompat:1.4.1'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.google.android.material:material:1.5.0'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.constraintlayout:constraintlayout:2.1.3'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.navigation:navigation-fragment-ktx:2.5.3'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.navigation:navigation-ui-ktx:2.5.3'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.legacy:legacy-support-v4:1.0.0'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'&lt;/span&gt;
    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'junit:junit:4.13.2'&lt;/span&gt;
    &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.test.ext:junit:1.1.3'&lt;/span&gt;
    &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.test.espresso:espresso-core:3.4.0'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;quede esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.core:core-ktx:1.8.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.appcompat:appcompat:1.4.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.google.android.material:material:1.5.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.constraintlayout:constraintlayout:2.1.3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.navigation:navigation-fragment-ktx:2.5.3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.navigation:navigation-ui-ktx:2.5.3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.legacy:legacy-support-v4:1.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;testImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"junit:junit:4.13.2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;androidTestImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.test.ext:junit:1.1.3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;androidTestImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"androidx.test.espresso:espresso-core:3.4.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
   (Best practice: Por ahorita, dejé las versiones de las dependencias "hardcoded", pero siempre tenlas en una variable adicional, en este caso los extras del paso 2.)
&lt;/h6&gt;

&lt;p&gt;Ahora, una vez terminado, sincroniza tu proyecto.&lt;/p&gt;

&lt;p&gt;Ya con esto habrás dejado actualizado tu proyecto para tenerlo con KTS y poder disfrutar de sus bondades, así como mantener actualizada tu aplicación.&lt;/p&gt;

&lt;p&gt;Espero que este tutorial les haya servido y de ser así compártanlo con más banda para que puedan ver más contenido que les estaré trayendo en este pequeño blog.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>android</category>
      <category>spanish</category>
      <category>kotlin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Visitando de Nuevo el Pokedex, con Jetpack Compose (Parte 1)</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Tue, 25 Apr 2023 13:38:14 +0000</pubDate>
      <link>https://dev.to/rzerostern/visitando-de-nuevo-el-pokedex-con-jetpack-compose-parte-1-57i8</link>
      <guid>https://dev.to/rzerostern/visitando-de-nuevo-el-pokedex-con-jetpack-compose-parte-1-57i8</guid>
      <description>&lt;p&gt;Otra vez dejándolos por mucho tiempo, mis niños adorados, pero bueno, ya nos encontramos aquí escribiendo para todos ustedes. Esperando que ya después de 4 meses de este 2023 todo les esté saliendo de maravilla y que no batallen mucho con los bugs.&lt;/p&gt;

&lt;p&gt;Si recuerdan, en un post anterior que les dejo &lt;a href="https://dev.to/rzerostern/post-mortem-el-pokedex-en-android-y-clean-architecture-2lc9"&gt;aquí&lt;/a&gt;, armamos el Pokedex y lo armamos con los siguientes parámetros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usamos Clean Architecture para conectar la API. En esa ocasión no usamos Room.&lt;/li&gt;
&lt;li&gt;Usamos RxJava para poder hacer los repositorios y usecases (interactors)&lt;/li&gt;
&lt;li&gt;Usamos Navigation Component para la navegabilidad.&lt;/li&gt;
&lt;li&gt;Usamos MVVM para poder estructurar las llamadas a la API desde nuestros Fragments o Activities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En esta ocasión, por fin he profundizado en el conocimiento de &lt;strong&gt;Jetpack Compose&lt;/strong&gt; para Android y he decidido comenzar a armar el Pokedex pero utilizando esta tecnología, al igual de hacer los siguientes cambios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Se usará Jetpack Compose desde el principio para generar todo el workflow de nuestra aplicación.&lt;/li&gt;
&lt;li&gt;Conservaremos la implementación de la Clean Architecture, pero ahora sí usaremos Room.&lt;/li&gt;
&lt;li&gt;Conservaremos el uso de MVVM, sin embargo ahora lo orientaremos a Jetpack Compose en lugar de Fragmentos. En los siguientes posts lo averiguaremos.&lt;/li&gt;
&lt;li&gt;Usaremos en lugar de RxJava, corrutinas, mismas que abonarán en el desarrollo de esta versión del Pokedex.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Oye, pero ¿qué diablos es Jetpack Compose?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faa94awt7fxebpx39aq7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faa94awt7fxebpx39aq7r.png" alt="Jetpack Compose Logo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jetpack Compose es el futuro, dirían algunos y sí, es un nuevo sistema de creación de interfaces incluído para Android, el cual arma de forma declarativa todas y cada una de nuestras interfaces gráficas. &lt;/p&gt;

&lt;p&gt;Esta forma de armar interfaces gráficas se ha establecido muy popularmente en SwiftUI (iOS) y Flutter (apps híbridas), por lo que ya prácticamente la curva de aprendizaje en cualquier sistema declarativo es muy similar y abona para poder aprender cualquiera de los otros dos, es decir, sabes Flutter, vas a agarrar Jetpack Compose y SwiftUI como pez en el agua (claro, sabiendo únicamente las sintaxis propias de cada framework).&lt;/p&gt;

&lt;h1&gt;
  
  
  ¿Cambia la generación de interfaces en Jetpack Compose?
&lt;/h1&gt;

&lt;p&gt;Definitivamente, como lo mencionamos anteriormente, Compose (así le diremos de ahora en adelante) utiliza el paradigma de programación declarativa, mismo que permite que una aplicación en vez de montar todos los componentes o widgets de la UI en un árbol de componentes, Compose únicamente aplique los cambios necesarios sin afectar el resto de la UI, haciéndolo más económico en cuanto a procesamiento y consumo de recursos.&lt;/p&gt;

&lt;p&gt;Ahora, unos ejemplitos.&lt;/p&gt;

&lt;p&gt;Históricamente, nosotros para generar un TextView, teníamos que hacer esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;TextView&lt;/span&gt;
    &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/text_view_id"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:text=&lt;/span&gt;&lt;span class="s"&gt;"@string/hello"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora en Compose, el declarar un TextView se hace de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Igualmente, si nosotros deseamos enviarle un parámetro a nuestra función Composable, lo podemos hacer y anidarla como cualquier variable en Kotlin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello $name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, hasta aquí todo muy bien, pero te has de preguntar ¿cómo es que lo mando llamar a mi interfaz? Pues de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;AppMainScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;scaffoldState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rememberScaffoldState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;coroutineScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rememberCoroutineScope&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nc"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;scaffoldState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scaffoldState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;topBar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;TopBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Pokedex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;buttonIcon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Filled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Menu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;onButtonClicked&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;coroutineScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;scaffoldState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;drawerContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;horizontalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CenterHorizontally&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El código arriba mencionado, es la forma predeterminada para mandar lo que antes conocíamos como un fragmento, sin embargo nos falta una parte importante: asegurarnos de que el punto de entrada, la Activity lance como tal nuestra pantalla, por lo que tenemos que modificar nuestra MainActivity a lo siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;PokedexTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;AppMainScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto podremos lanzar nuestra primer aplicación móvil con Jetpack Compose y se verá así:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv42rwoi5i958jm3cmmud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv42rwoi5i958jm3cmmud.png" alt="Image description" width="800" height="1688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el siguiente post, voy a profundizar en armar la Toolbar (que ya está en la pantalla anterior), pero quisiera explicar funciones necesarias para controlar la navegabilidad dentro de Jetpack compose y otras features que tenemos. Asimismo, ya estaremos construyendo algo un poquito más elaborado en cuanto a interfaces se refiere. Stay tuned.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>spanish</category>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Hackathones: ¿Por qué me retiré de ellos?</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Tue, 25 Oct 2022 11:43:11 +0000</pubDate>
      <link>https://dev.to/rzerostern/hackathones-por-que-me-retire-de-ellos-3dfc</link>
      <guid>https://dev.to/rzerostern/hackathones-por-que-me-retire-de-ellos-3dfc</guid>
      <description>&lt;p&gt;¡Mis niños adorados!&lt;/p&gt;

&lt;p&gt;Ya tiene tiempo que no les escribo y en parte es porque el trabajo no me ha dejado, pero ahorita que el insomnio me está dando con todo, aprovecho para escribir un ratito. &lt;/p&gt;

&lt;p&gt;En esta ocasión he de escribir algo para hacer catarsis y ya dejarlo ir de mi mente (y no es un clásico post sobre ayuda psicológica, ni soy psicólogo ni este es el lugar), solo quiero platicarles mi punto de vista y opinión sobre los &lt;strong&gt;hackathones&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Insisto, es mi opinión y recuerden lo que dicen &lt;em&gt;las opiniones son como el c*lo, todos tenemos uno&lt;/em&gt;, así que venga de ahí.&lt;/p&gt;

&lt;h1&gt;
  
  
  ¿Qué ch******* es un hackathon?
&lt;/h1&gt;

&lt;p&gt;Comencemos por definir un hackathon, este es un evento en donde se reúnen profesionales de TI, marketing, UX/UI, PM's y demás disciplinas y arman equipos para poder desarrollar una solución que sea benéfica para el organizador en el tiempo que dura el evento a cambio de un premio o una compensación. Que eso de la compensación es a veces y eso si el organizador tiene con qué, porque luego hay gandules que te dan como premio las gracias o como decimos en México &lt;em&gt;"una larga vida y dos costales de oro"&lt;/em&gt;. De esos ni vale la pena hablar.&lt;/p&gt;

&lt;p&gt;Una vez definido esto, podemos comenzar a ver una problemática en este tipo de eventos: primeramente el estar en un hackathon es asumir que estás trabajando para el organizador. &lt;strong&gt;LITERAL&lt;/strong&gt;. Es chingarle para esa persona o empresa hasta 72 horas sin parar (tocaré ese tema en breve) para poder tener una solución &lt;em&gt;que le guste al organizador&lt;/em&gt; (también tocaremos eso) y así poder hacerte de un dinerito extra para ti y los miembros de tu team. Si no ganas, date de santos que en los términos y condiciones no te metan un golazo de que lo que hayas desarrollado pase a ser propiedad del organizador (aunque eso se estilaba al principio, ahorita ya no por el conocimiento que tiene la banda sobre sus derechos como creadores de software y contenido).&lt;/p&gt;

&lt;h1&gt;
  
  
  ¡Buenísimo horas extra!
&lt;/h1&gt;

&lt;p&gt;Como lo mencioné anteriormente, el estar en un hackathon es trabajar horas extra, es estar en friega loca para poder sacar una solución en base a una idea que te da el comité organizador, empresa, etc. Lo que llega a suceder es que en estos eventos lo que piden es &lt;em&gt;una demo&lt;/em&gt; de la solución que estás proponiendo. &lt;/p&gt;

&lt;p&gt;Así es, tienes que mostrar una demo de tu producto (por muy pinche  que esté e incluso si lo quieres programar a la Bolognesa, no hay problema) y por lo general ponerte de acuerdo con los miembros del team para que realicen las tareas que necesitan. En la parte de hacer tus pininos como líder podría decir que te enriquece y te hace pensar ya no de forma individualizada, pero ahorita les cuento sobre eso. Regresemos a los entregables.&lt;/p&gt;

&lt;p&gt;Las horas trabajando en el desarrollo suelen ser demasiado extenuantes si es que no llevas control de tus horas ni de la actividad en sí, puedes llegar a quedarte codeando día y noche y perder noción del tiempo, lo cual resulta ser demasiado contraproducente y al momento de la premiación del evento, si no ganas te llega una frustración como ninguna, puesto que el esfuerzo que diste para con el evento tiene un sublime y hermoso &lt;strong&gt;&lt;em&gt;siga participando&lt;/em&gt;&lt;/strong&gt; y más cuando los organizadores literalmente escogen a los ganadores sin ver &lt;strong&gt;las viabilidades de implementación o solamente la eligen por razones fuera de lo técnico y del enfoque de negocio&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sí, en los hackathones se lleva uno demasiado esfuerzo, pero también grandes enseñanzas, comencemos por los cuentos de terror que me han sucedido.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cuentos desde la cripta de Hackathones
&lt;/h1&gt;

&lt;p&gt;En mi carrera participé de al menos 5 hackathones, unos pasaron sin pena ni gloria, otros no. Pero en dos, fueron como catastróficos y que ya después de algo de tiempo puedo sacar a la luz. No ventilaré marcas (mas que las necesarias para fines comparativos) ni nombres, pero quienes me conocen sabrán qué pedo y nuevamente les digo que es mi más sincera opinión, muchos tendrán la propia y pues qué importa, ahí les voy.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Eres juez de hackathon o de concurso de belleza?
&lt;/h3&gt;

&lt;p&gt;Por ahí de 2018, entré a un "popular" Hackathon de CDMX con algunos amigos, incluso estuvo buenísimo en el ámbito de convivencia pues fue el primero en el que le dediqué el fin de semana completo a estar allá (yo vivo a 45 minutos del centro de la CDMX, pero se le considera Zona Metropolitana), por lo que compré hospedaje para 3 días, en ese entonces no había Airbnb y lo compré en el Holiday Inn de la Zona Rosa (¡FIESTA!).&lt;/p&gt;

&lt;p&gt;El entusiasmo estaba desbordado, puesto que era la primera vez que tenía equipo completo y todos compartíamos la ilusión en ese momento de poder ganarnos la suma de 150 mil pesos (USD $7,500).  La organización del evento, impecable, todo en orden. Conexión a Internet, muy buena. Seguridad, absoluta. En cuanto a organización hablando estrictamente de ser anfitrión, muy por encima (es lo que salva al hackathon de una hecatombe segura).&lt;/p&gt;

&lt;p&gt;Vayamos con la solución: La solución que se pedía en ese entonces era de poder "democratizar" el sector bancario, por lo que en ese momento, propusimos el poder dar y recibir pagos por medio de un código QR con una aplicación móvil (¿les suena eso? Aclaro, para nada soy el inventor de ello, sino que quedó en una propuesta para esa empresa). Esto sin importar el cliente, incluso podrías operarlo con un teléfono celular de gama baja. Era sensacional la idea, incluso a empleados de la empresa les encantaba la idea.&lt;/p&gt;

&lt;p&gt;Pero como dice Polo Polo: &lt;em&gt;no falta el hijito de la ch******&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Llegó el momento de presentar la idea a los "jueces", en ese momento, los nervios me invadieron pues era la primera ocasión que me echaba al hombro el trabajo de todo mi team y que exponía la idea a otras personas. Creía tenerlo todo bajo control, preguntas, cómo podría esto beneficiar a los cuentahabientes, cómo podrías provisionar la aplicación, etc. Todo en orden, de hecho uno de los jueces (eran 2) estaba convencido de que la idea podía pasar a la etapa del &lt;em&gt;pitching&lt;/em&gt;, pero el "juez" 2, salió con una pregunta que créanme que es de mentada de madre:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿POR QUÉ DEBERÍAS GANAR ESTE HACKATHON?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mi equipo se quedó helado. Yo peor. Contesté lo primero que me venía a la mente, la verdad. Pero cuando los jueces agradecieron y se fueron varios nos quedamos de ¿Pues qué no era un hackathon? y uno de los miembros de mi team tiempo después me dijo &lt;em&gt;ni que hubiéramos estado en Miss Universo&lt;/em&gt;. Si bien mi reacción a la pregunta no fue la correcta y aprendí que esos individuos hacen esas preguntas para hacerte flaquear, también la pregunta fue una reverendísima ma****. Si hoy me hicieran esa pregunta les contestaría: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Pues mira, la razón principal es porque mi equipo no se está quemando las pestañas gratis.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Obviamente, perdimos. &lt;/p&gt;

&lt;p&gt;Lo peor no paró ahí, lo peor vino cuando pues la gran mayoría, ya sin ilusiones (pero sí con mucho swag), quería retirarse del recinto, pero teníamos que quedarnos a la premiación. Obviamente, ya molestos y cansados, nadie de los equipos que no pasaron quería y empezó la silbatina, las mentadas de madre y pues hasta tuvieron que llamar la atención los del comité organizador, pero poco pudieron lograr. Terminaron dejándonos ir.&lt;/p&gt;

&lt;p&gt;Ese día acabó en una pizzería de Buenavista. Ya mucho mejor que los 3 días de desvelo.&lt;/p&gt;

&lt;h3&gt;
  
  
  LO ELIJO POR MIS 🥚🥚
&lt;/h3&gt;

&lt;p&gt;En 2021, se volvió a armar otra vez ese popular hackathon (cualquiera diría &lt;em&gt;S.S.D.D.&lt;/em&gt;, pero decidí darle un voto de confianza). En esta ocasión se decidió que iba a ser remoto y el equipo fue muy ameno. Aunque el requerimiento era de 6 personas, terminamos siendo 5, puesto que la sexta persona nos quedó mal al final.&lt;/p&gt;

&lt;p&gt;El primer día recuerdo que me puse en contacto con el líder de la vertical a la que nos habíamos inscrito para que nos diera a entender qué era lo que buscaban. Se trataba de hacer una "super-app", sin embargo nos dijo que por el tipo de negocio que la empresa manejaba, no pueden ser como otras super-apps del mercado como Baz o MercadoPago, pero que querían ofrecer una app tipo WeChat.&lt;/p&gt;

&lt;p&gt;Nos esforzamos en sacar la idea, que aunque no era como para quedar ganadora, sí tenía mucho potencial y sabíamos que por lo menos podría quedar considerada. En esta ocasión, me tocó desempeñarme, además de representante del team, como desarrollador iOS y pues como &lt;em&gt;Dios me dió a entender&lt;/em&gt;, se hizo el desarrollo de la app.&lt;/p&gt;

&lt;p&gt;Siento en lo personal que todo el equipo se rifó en lo que pedía. En 2020, valimos gorro porque definitivamente no entregamos (hay que saber reconocer cuando hacemos las cosas mal y tenemos la culpa). Pero en este 2021 fue una "redención" y al entregar todo en tiempo y forma era un aliciente.&lt;/p&gt;

&lt;p&gt;Llegó el día de la premiación. 72 horas como locos armando diversas soluciones y era tiempo de saber quién era el ganador. Fueron pasando ganador por ganador, hasta llegar a la vertical de super-app. Nos quedamos otra vez helados por la decisión:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La superapp ganadora &lt;strong&gt;tenía todo lo que el líder dijo que no se podía poner&lt;/strong&gt;. Todo lo que el líder de la vertical me dijo que no podía contener, la contenía.&lt;/li&gt;
&lt;li&gt;No era aplicación móvil (es más, ni se molestaron en hacerla así).&lt;/li&gt;
&lt;li&gt;UX/UI deplorable (hasta hay becarios que hacen mucho mejor trabajo), pero se comprende, en estos eventos lo que más falta es personal de UX/UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los reclamos no se hicieron esperar. Muchos equipos se quejaron y reclamaron precisamente eso, a lo que los organizadores se limitaron a decir:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Se tenía que entregar un prototipo, por lo que se entiende la falta de calidad del mismo.&lt;/em&gt; (O sea podías entregar puras cochinadas y no había problema.)&lt;/p&gt;

&lt;p&gt;Obviamente, como en estos tiempos sucede, comenzaron a tildarnos de haters. Pero lo gacho no terminaba, no conforme con elegirlos ganadores de la vertical, ese "prototipo" ganó el hackathon de ese año. Fue cuando se armó un hervidero el hilo de comentarios, donde muchísimos (incluyéndome) sí les reclamamos que las disposiciones y condiciones que habían puesto &lt;em&gt;se las pasaron por el Arco del Triunfo&lt;/em&gt; (a la fecha sigo pensando eso). Al día de hoy, la transmisión de esta premiación ocultó los comentarios de dicho hilo.&lt;/p&gt;

&lt;p&gt;Terminó la transmisión y decidí dejarle mi queja a la empresa y los organizadores (misma que terminó en la máquina de escribir invisible de la empresa y de los organizadores). Una vez hecho eso, me fuí a dormir.&lt;/p&gt;

&lt;h1&gt;
  
  
  No todo es Viernes 13
&lt;/h1&gt;

&lt;p&gt;Pero no todo es zozobra, también he tenido triunfos en hackathones. En un hackathon que participé con otro colega, ganamos 10 mil pesos (USD $500) por realizar una aplicación en la que se pudieran fomentar las artesanías del país, misma que hicimos como prototipo en 2 días. &lt;/p&gt;

&lt;p&gt;Fue muy ameno y nos trataron muy bien en ese hackathon, que aunque tuve que ir de mi casa a las oficinas de Google 2 días seguidos, fue muy satisfactorio el haber desarrollado esa pequeña solución. Lamentablemente ya no pudimos sacarla bien a producción por falta de inversión y de tiempo. Abandoné el coworking donde estaban las empresas involucradas y ya no los pude contactar (el error más garrafal de toda mi carrera, luego les contaré).&lt;/p&gt;

&lt;p&gt;El destino de ese premio solo lo sabemos los involucrados. No lo compartiré ;).&lt;/p&gt;

&lt;h1&gt;
  
  
  Epílogo
&lt;/h1&gt;

&lt;p&gt;Luego del hackathon de 2021, me retiré de este tipo de eventos. Comprendí que ya no estoy para malpasarme a lo tonto, mucho menos por empresas que tienen cierto desdén por las reglas. Ahora trato de terminar un par de side projects, que esperemos en Dios así sea, así como a dedicarme a mi mismo en la parte profesional y en la personal (aprendí a ser mejor líder). Igualmente aprendí varias cosas de todo este berenjenal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A veces se gana y a veces se pierde, pero molesta bastante perder con dados cargados o por causas estúpidamente sencillas.&lt;/li&gt;
&lt;li&gt;A pesar que otros rompan las reglas, mantente firme y recto en el trabajo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y la más importante:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Si te vas a esforzar y malpasar por algo, que ese algo sea para tu beneficio.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Espero que no los haya aburrido con este post y si leyeron hasta acá, muchísimas gracias por su apoyo. Espero ya no tener abandonado este espacio, pues veo que mis posts están pegando cool. Nos vemos en la próxima.&lt;/p&gt;

&lt;p&gt;Sternie out.&lt;/p&gt;

&lt;p&gt;P.D: Las ideas rechazadas que menciono aquí ya otras empresas las han desarrollado y les va muy de poca madre. Una de cal por las que van de arena.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>career</category>
      <category>discuss</category>
      <category>community</category>
    </item>
    <item>
      <title>100 Days of Code: Día 1</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Sat, 02 Oct 2021 19:25:31 +0000</pubDate>
      <link>https://dev.to/rzerostern/100-days-of-code-dia-1-4oco</link>
      <guid>https://dev.to/rzerostern/100-days-of-code-dia-1-4oco</guid>
      <description>&lt;p&gt;Qué bueno que ya se me está haciendo costumbre esto de escribir unas cuantas líneas en mi perfil y pues el día de hoy, como les mencioné en mi Twitter, voy a estar en el reto #100DaysOfCode para poder fortalecer mis skills y estar aún más al tiro en lo último de código. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/MQ5TBGnQOFeMg/giphy-downsized-large.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/MQ5TBGnQOFeMg/giphy-downsized-large.gif" alt="Smith-Oracle" width="480" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿En qué consiste?
&lt;/h2&gt;

&lt;p&gt;El reto consiste en programar al menos durante 1 hora diaria durante 100 días, con el fin de poder crear o reforzar conocimientos adquiridos previamente y publicando dichos avances durante la misma cantidad de días. Muchos solo lo hacen por Twitter, sin embargo otros más lo hacen en sus respectivos canales de YouTube, Twitch, DEV.to, etc.&lt;/p&gt;

&lt;p&gt;Las reglas del reto son las siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Programa 1 hora durante 100 días&lt;/li&gt;
&lt;li&gt;Publica tus avances&lt;/li&gt;
&lt;li&gt;Motiva a 2 personas al día a que comiencen el reto.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si quieres saber más sobre esta iniciativa, visita &lt;a href="https://www.100daysofcode.com/" rel="noopener noreferrer"&gt;https://www.100daysofcode.com/&lt;/a&gt;, la página oficial de la iniciativa.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cuál es tu objetivo para esta iniciativa?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/oj2GhTqAIoNIk/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/oj2GhTqAIoNIk/giphy.gif" alt="I'm going on an adventure !!!" width="480" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pues bien, ha habido varios rubros que necesito reforzar como desarrollador, entre los cuales veo los siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iOS con Swift&lt;/li&gt;
&lt;li&gt;React (JS, Native y Gatsby)&lt;/li&gt;
&lt;li&gt;Angular (actualizarme)&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Microservicios&lt;/li&gt;
&lt;li&gt;Uso de Cloud Development (Lambdas)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Igualmente, les dejo el enlace al repositorio de la bitácora para que puedan checar más a detalle los avances en &lt;a href="https://github.com/RZEROSTERN/100-days-of-code" rel="noopener noreferrer"&gt;https://github.com/RZEROSTERN/100-days-of-code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por lo tanto, para este día 1 ¿qué les parece si nos ensuciamos un poquito las manos con &lt;strong&gt;iOS&lt;/strong&gt;? Para esta ocasión, armaremos un proyecto &lt;em&gt;Hello World!&lt;/em&gt; con Swift :3. A más tardar, el día de mañana les comentaré las actualizaciones del día.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update: CocoaPods
&lt;/h2&gt;

&lt;p&gt;El proyecto en cuestión he podido prepararlo e implementar CocoaPods. Para los que no van empezando, CocoaPods es el repositorio de librerías para iOS, con él puedes tener las capacidades que tiene Android con Maven. Está hecho con Ruby y da mayor flexibilidad a tu desarrollo iOS.&lt;/p&gt;

&lt;p&gt;Para instalar CocoaPods tienes que hacer lo siguiente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instala CocoaPods en tu Mac con el siguiente comando:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;cocoapods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Crea un proyecto de app en XCode.&lt;/li&gt;
&lt;li&gt;Cierra XCode y abre una terminal.&lt;/li&gt;
&lt;li&gt;Ejecuta los siguientes comandos a nivel de la &lt;strong&gt;carpeta raíz de tu proyecto&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pod init
pod &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente abre el archivo &lt;strong&gt;.xcworkspace&lt;/strong&gt; que se ha creado. Al haber instalado CocoaPods correctamente, observarás que ha cambiado el layout del proyecto al siguiente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5dtuf92hmh3khths2no.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5dtuf92hmh3khths2no.png" alt="Screen Shot 2021-10-04 at 10.36.47" width="258" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora sí podemos comenzar con el desarrollo de nuestra app en iOS y concluir con el día 1 de #100DaysOfCode. Lo que prosigue es hacer la pantalla de splash y comenzar con el diseño de una interfaz dentro del Storyboard de iOS.&lt;/p&gt;

&lt;p&gt;Happy coding !!!&lt;/p&gt;

</description>
      <category>100daysofcode</category>
      <category>spanish</category>
      <category>ios</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Mi primer package en Flutter y contribución al Open Source, en HACKTOBERFEST</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Fri, 01 Oct 2021 19:48:43 +0000</pubDate>
      <link>https://dev.to/rzerostern/mi-primer-package-en-flutter-y-contribucion-al-open-source-en-hacktoberfest-4a1l</link>
      <guid>https://dev.to/rzerostern/mi-primer-package-en-flutter-y-contribucion-al-open-source-en-hacktoberfest-4a1l</guid>
      <description>&lt;p&gt;Mis niños, estoy de regreso como de costumbre y ahora en este mes de Octubre tenemos la novedad de todos los años que es el Hacktoberfest, de lo que les platicaré en el siguiente post sobre mis patoaventuras en contribuir al software libre. Sí, por fin me animé a contribuir. &lt;/p&gt;

&lt;p&gt;Primeramente, nunca había colaborado con el mundo del open source, pues en algunas ocasiones he pensado que algunos desarrolladores desmotivan el participar en ello al imponer sus dogmas como la &lt;strong&gt;&lt;em&gt;verdad absoluta&lt;/em&gt;&lt;/strong&gt; y que debe seguirse al pie de la letra o el denostar el trabajo de otros desarrolladores solo porque sí. Esto ha sido siempre motivo de muchas discusiones y dramas pues los llamados &lt;strong&gt;&lt;em&gt;cuidarranchos&lt;/em&gt;&lt;/strong&gt; o &lt;strong&gt;&lt;em&gt;gatekeepers&lt;/em&gt;&lt;/strong&gt; siempre andan a la orden del día y van a querer fastidiarte el día. &lt;/p&gt;

&lt;p&gt;El &lt;strong&gt;gatekeeper&lt;/strong&gt; es el clásico &lt;em&gt;sabelotodo&lt;/em&gt; que siente que se las sabe de todas y que es el auténtico master y al que muchos (por miedo o lo que sea) veneran, siendo su especialidad el estar criticando de forma muy airada e incluso hasta agresiva a los que quieren cambiar algún estándar, código, modus operandi o hasta modelo de negocio. Si me piden ponerlo en una imagen, que South Park nos ayude:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Gatekeeper in a nutshell&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbenutbldi7nlfwn4i6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbenutbldi7nlfwn4i6n.png" alt="Gatekeeper in a nutshell" width="501" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora, la segunda causa por la que no entraba es referente al &lt;strong&gt;síndrome del impostor&lt;/strong&gt; que muchos de nosotros manejamos como desarrolladores. El síndrome del impostor es el creer que no puedes hacer ciertas cosas justo cuando ya las has hecho o crees que has defraudado a las personas por hacer un mal trabajo cuando es prácticamente lo contrario. Recientemente me ha tocado colaborar en múltiples desarrollos tanto de mi trabajo como de freelance y de side projects y ya poco a poco se me ha ido quitando eso, aunque al ver desarrollos mejores llega a reaparecer, pero aquí ya es más de comprender que hay mejores desarrolladores que uno.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué has hecho con el Open Source ahorita?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=vHhDKRgO1Oc" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKWWS85r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.youtube.com/vi/vHhDKRgO1Oc/0.jpg" alt="Trabajo muy duro, como un esclavo" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verán, al entrar al desarrollo con Flutter, me he encontrado con que algunas librerías no existen o como todo en este mundo de la programación, puede llegar a tener huecos de seguridad o simplemente es obsoleta.&lt;/p&gt;

&lt;p&gt;En este caso una vez me habían pedido en realizar una aplicación de posts en la que pudiéramos dar soporte a formato WYSIWYG (What You See Is What You Get, para los que no conocían el acrónimo) y comenzó a usar mi compita &lt;a class="mentioned-user" href="https://dev.to/kiramishima_3"&gt;@kiramishima_3&lt;/a&gt; EditorJS en su backend. Cuál fue mi sorpresa de que no había en Flutter una librería que hiciera eso y pues me tocó hacerla desde cero.&lt;/p&gt;

&lt;p&gt;Dentro de la misma he podido ya agregar elementos como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selección de fuente&lt;/li&gt;
&lt;li&gt;Creación de párrafos&lt;/li&gt;
&lt;li&gt;Inserción de imagenes, ya sea de la cámara o de la galería&lt;/li&gt;
&lt;li&gt;Barra de estados&lt;/li&gt;
&lt;li&gt;Retroalimentar un JSON tipo EditorJS a un contenedor&lt;/li&gt;
&lt;li&gt;Obtención de información JSON desde un archivo estático o una API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ahorita ¿cuál es el destino del package?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pcmc33a9h5u5ikv2zlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pcmc33a9h5u5ikv2zlk.png" alt="Banner people developing" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pues bien, he de decirles que no he podido terminar, dado a que me atiborraron con muchísimos proyectos, gracias a Dios. &lt;/p&gt;

&lt;p&gt;Sin embargo, en este Hacktoberfest decidí liberarlo como &lt;em&gt;alpha&lt;/em&gt; para poder ir agregando las features restantes y compatibilidades ad-hoc a los estándares del desarrollo Flutter y siento que la &lt;strong&gt;comunidad open source&lt;/strong&gt; puede apoyarme en estos momentos en los que este proyecto, que se ve prometedor, pueda servir para muchos desarrolladores. &lt;/p&gt;

&lt;p&gt;El Hacktoberfest es un evento anual donde los desarrolladores, empresas, entusiastas open-source, etc, deciden compartir desarrollos y ponerlos a disposición de la comunidad para así poder expandir los mismos. A cambio, si haces 4 Pull Requests válidos tienes derecho a una playera y swag del evento. Muchos dirán &lt;em&gt;¿trabajas por una playera?&lt;/em&gt;, yo digo &lt;em&gt;sí ¿por qué no? Es divertido y me gusta&lt;/em&gt; y eso en lo personal es lo que me motiva a seguir siendo ingeniero de software. Les dejo el link del repositorio para que colaboren &lt;a href="https://github.com/RZEROSTERN/editorjs-flutter" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Una vez que ya la librería tenga una forma más estable se publicará en pub.dev (repositorio de librerías para Flutter y Dart) para que pueda usarse en desarrollos retail. Solo esperemos que la banda &lt;em&gt;gatekeeper&lt;/em&gt; no se ponga sus moños y no nos quieran funar :P.&lt;/p&gt;

&lt;p&gt;Pues bien, como de costumbre, muchísimas gracias por leerme y no olviden darle amor a este y el resto de mis posts y pronto estaré de regreso con el streaming. Stay tuned.&lt;/p&gt;

&lt;p&gt;Happy coding !!!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>hacktoberfest</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Pruebas Unitarias en Autenticación con Laravel y Passport</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Mon, 13 Sep 2021 17:57:55 +0000</pubDate>
      <link>https://dev.to/rzerostern/pruebas-unitarias-en-autenticacion-con-laravel-y-passport-97h</link>
      <guid>https://dev.to/rzerostern/pruebas-unitarias-en-autenticacion-con-laravel-y-passport-97h</guid>
      <description>&lt;p&gt;Mis niños, la verdad, así como los he dejado solos un buen rato, ahora ando inspirado para publicar algo que me trajo muchos dolores de cabeza: las pruebas unitarias con Passport, no sin antes, quiero felicitarlos hoy por el día del programador a todos y cada uno de mis colegas y de la comunidad DEV en español.&lt;/p&gt;

&lt;p&gt;Para los que no conocen Passport, es una librería dentro de Laravel que te permite realizar autenticación con OAuth 2 mediante Personal Access Tokens o por medio de las clásicas JWT junto con una Refresh Token para mantener el login el mayor tiempo posible.&lt;/p&gt;

&lt;h2&gt;
  
  
   El rollo de implementar UT
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.makeagif.com%2Fmedia%2F12-17-2014%2FiP6A8a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.makeagif.com%2Fmedia%2F12-17-2014%2FiP6A8a.gif" alt="ANGRY GERMAN KID"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando uno está acostumbrado a desarrollar sin Unit Testing, por lo general se vuelve un dolor de cabeza el implementarlas, sin embargo conforme va uno agarrando el hilo de las mismas, además de que es una economía al momento de poner en producción un desarrollo te da la certeza de que desarrollaste un módulo correctamente.&lt;/p&gt;

&lt;p&gt;Pero antes que nada dirán los que empiezan ¿qué es una prueba unitaria? Ah pues como lo dice la gran amiga Wikipedia &lt;em&gt;una prueba unitaria es una forma de comprobar el correcto funcionamiento de una unidad de código&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Entonces, cuando nosotros hacemos algún comportamiento dentro de un sistema, tenemos que pasarlo por pruebas diversas, sin embargo, las que nos alertan desde un principio si están bien son las unitarias. En PHP (lenguaje de Laravel) usamos lo que es PHPUnit para realizar las mismas y este por lo general ya viene incluido cuando instalas Laravel.&lt;/p&gt;

&lt;p&gt;El tema viene cuando quieres hacer unit testing con Passport. Puesto que en uno de los pasos, tienes que llamar al servidor para generar tus respectivas tokens. Sin embargo sucede que tu UT va a tronar si careces de un servidor web implementado en la misma. Veamos qué sucede.&lt;/p&gt;

&lt;p&gt;Ahora bien, si quieres saber más a fondo sobre cómo implementar Passport en Laravel, te dejo este &lt;a href="https://dev.to/azibom/create-api-rest-with-laravel-7-x-passport-authentication-and-implement-refresh-token-part-1-43ja"&gt;link&lt;/a&gt;, mismo que es en el que me basé para armar este post.&lt;/p&gt;

&lt;h2&gt;
  
  
  El código a testear
&lt;/h2&gt;

&lt;p&gt;Primeramente, tomamos en cuenta el siguiente método:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAuthAndRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nv"&gt;$oClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password_client'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$oauthUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_ENV'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'passport.token'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_URL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'oauth/token'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$http&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$oauthUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'form_params'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'grant_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'client_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'client_secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'scope'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;successStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La implementación en el método a testear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nv"&gt;$validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'email|required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required'&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fails&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'details'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAuthAndRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'Unauthorized'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y su respectiva UT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_login_success&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;postJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/api/login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertJson&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al momento de nosotros realizar nuestras pruebas unitarias, van a fallar irremediablemente y la razón es de que no hay como tal una implementación de un servidor web existente dentro de PHPUnit. Nosotros para poder realizar nuestras pruebas, tenemos que &lt;strong&gt;simular&lt;/strong&gt; el request dentro del primer método y precisamente el bloque siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$http&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$oauthUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'form_params'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'grant_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'client_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'client_secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'scope'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;es el que nos está dando lata :(. Si lo pasas por un Github Actions, va a tronar riquísimo marcándote que no existe la URL que tienes en la siguiente línea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$oauthUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_ENV'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'passport.token'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_URL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'oauth/token'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  La solución a todos nuestros problemas
&lt;/h2&gt;

&lt;p&gt;Primeramente, el método que tenemos que refactorizar es el de getAuthAndRefreshToken. Tenemos que cambiar el modo como llamamos el request y que sea funcional tanto en la implementación como en las pruebas. &lt;/p&gt;

&lt;p&gt;La forma de realizarlo es:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Asignando al request principal los parámetros que el endpoint OAuth requiere para armar un nuevo request.&lt;/li&gt;
&lt;li&gt;Despachar una ruta en POST: el endpoint &lt;em&gt;oauth/token&lt;/em&gt; por medio de un nuevo Request interno.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lo anterior sin utilizar cliente alguno como Guzzle o similares, mismas que requieren tener un servidor Apache o nginx implementado, features que no se utilizan en pruebas unitarias.&lt;/p&gt;

&lt;p&gt;Para ello cambiamos a lo siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getAuthAndRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$oClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password_client'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DEFAULT_PASSWORD_CLIENT_ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'grant_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'client_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'client_secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$oClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'scope'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'oauth/token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este refactor, hemos cambiado varias cosas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ya no necesitarás una URL ni un cliente para armar el request. Es más sano, pues ya así puedes asignar la implementación sin importar la URL o factores externos al método y puedes testear sin problemas desde consola.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Obtienes mayores detalles de la respuesta que te da Passport al momento de llamar el endpoint de OAuth para poder dar más transparencia al proceso, igualmente ya podrás hacer manejo de errores del método sin problemas.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Y ya con este refactor, tendrías que cambiar la implementación en tu método login, concretamente el inicio del método ya sería así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAuthAndRefreshToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al terminar todo esto, puedes pasar tus unit testings de forma manual en tu código o por Github Actions para poder confirmar las mismas y ya proceder a realizar implementaciones adicionales.&lt;/p&gt;

&lt;p&gt;Y pues bien, esto era lo que me traía de los pelos en cuestiones de pruebas unitarias y quiero compartirlo con ustedes para que puedan sacar más rápido este tema con Passport. Ahora, no olvides de reaccionar y compartirlo con los colegas para que haya más y mejores posts en español dentro de DEV.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>laravel</category>
      <category>passport</category>
      <category>testing</category>
    </item>
    <item>
      <title>Wizeline y yo: De reclutamiento en mobile y un objetivo cumplido</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Mon, 06 Sep 2021 15:17:34 +0000</pubDate>
      <link>https://dev.to/rzerostern/wizeline-y-yo-de-reclutamiento-en-mobile-y-un-objetivo-cumplido-1a04</link>
      <guid>https://dev.to/rzerostern/wizeline-y-yo-de-reclutamiento-en-mobile-y-un-objetivo-cumplido-1a04</guid>
      <description>&lt;p&gt;Mis niños, ya hace rato que no escribo para ustedes. De verdad les pido una disculpa, sin embargo he estado algo atareado con este nuevo reto que les he de platicar en estas líneas. &lt;/p&gt;

&lt;p&gt;En esta ocasión me gustaría platicarles sobre reclutamiento y cómo obtener trabajo en el ámbito móvil; algunas cosas aplicarían tanto para Android como para iOS, sin embargo seré más específico para la parte de Android, la cual es mi skill principal al día de hoy junto con PHP.&lt;/p&gt;

&lt;p&gt;Pues ahora sí, al grano. Les quiero platicar sobre cómo fue que entré a &lt;strong&gt;Wizeline&lt;/strong&gt;, una de las empresas más importantes en el ramo de la ingeniería de software hoy en día y que poco a poco ha ido creciendo en dicho ámbito. En verdad, a un mes de haber entrado, sigo sin creermela de estar en estos momentos siendo parte de esta gran empresa. Les platicaré aspectos sobre todo que les pueden servir a quienes quieren entrar en el ámbito mobile y de cómo poder formar parte no solo del equipo de Wizeliners, sino de cualquier otro team. &lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cómo conocí a Wizeline?
&lt;/h2&gt;

&lt;p&gt;A Wizeline lo conocí cuando estaba trabajando con Kira y Chemasmas en Iterando. Recuerdo muy bien que se encontraban sus oficinas a 2 cuadras de las de COW Roma y muchas comunidades tomaban a Wizeline como sede de sus Meetups. Llegamos a ir a algunas de ellas y se llegaba a armar una que otra &lt;em&gt;workshop&lt;/em&gt; sobre algún lenguaje de programación (JS, PHP, Java, etc) y andábamos de &lt;em&gt;pata de perro&lt;/em&gt; visitando las Meetup.&lt;/p&gt;

&lt;p&gt;Fue precisamente durante una de ellas en donde se ofrecieron vacantes para poder ingresar a la empresa, mismas a las que en lo personal acepté entrar, sin embargo...&lt;/p&gt;

&lt;h2&gt;
  
  
  Primeros Intentos: WASTED
&lt;/h2&gt;

&lt;p&gt;Las primeras veces que llegué a aplicar a Wizeline, no les niego que fue mi &lt;em&gt;reality check&lt;/em&gt; como desarrollador, pues conocí un paso dentro del proceso de reclutamiento que se ha vuelto un verdadero Calvario hasta el día de hoy: &lt;strong&gt;la prueba de algoritmos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9674em175s51kuv57g0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9674em175s51kuv57g0p.png" alt="image" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El conocer las pruebas de algoritmos fue el parteaguas para poder aprender un poco más de estructuras de datos y para poder ver otros procesos de reclutamiento como los de las FAANG en donde inclusive llega a ser el clásico &lt;em&gt;white board&lt;/em&gt;. Sin embargo, como les mencioné antes, estas pruebas no son mi fuerte. Así que ya sabrán cómo fueron los primeros resultados.&lt;/p&gt;

&lt;p&gt;En varias ocasiones fuí rechazado para entrar como frontend y backend developer y hubo un tiempo en el que dije &lt;em&gt;toda esperanza está perdida&lt;/em&gt;. Hasta que llegó la pandemia, el 2021 y una nueva oportunidad.&lt;/p&gt;

&lt;h2&gt;
  
  
  El reclutamiento
&lt;/h2&gt;

&lt;p&gt;Después de mucho tiempo, en este 2021 volví a obtener una nueva oportunidad para entrar y en esta ocasión las cosas cambiaron muchísimo:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DISCLAIMER: He de decir que este fue el proceso que seguí para &lt;strong&gt;mobile&lt;/strong&gt; y puede cambiar dependiendo de las diferentes ramas que se requieren en Wizeline, sobre todo con las pruebas técnicas.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Primeramente fue un screening con RR.HH. en donde conoces a la empresa y ellos te conocen a ti. En este caso es una llamadita de 30 minutos donde platicas sobre temas referentes a tu experiencia profesional. En mi caso la entrevista fue muy fluída y hasta el tiempo se pasó por 5 o 10 minutos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Una vez que pasas esta entrevista, lo siguiente son las &lt;strong&gt;pruebas de inglés&lt;/strong&gt;. Sí, en Wizeline, por la naturaleza de los proyectos, se considera como &lt;strong&gt;must&lt;/strong&gt; tener inglés de &lt;em&gt;upper intermediate&lt;/em&gt; para arriba, por lo que se hace una entrevista en inglés 100% de igualmente 30 minutos, así como una pequeña prueba de gramática hecha por una plataforma web. La ventaja es que esta entrevista es completamente separada de las demás, por lo que no tienes que preocuparte de estar estresado por una entrevista anterior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Si todo sale bien en esta entrevista, se vienen un par de entrevistas adicionales: &lt;strong&gt;las entrevistas técnicas&lt;/strong&gt;. Estas tienen la finalidad de poner a prueba tus skills en la tecnología a la que aspiras (sea iOS o Android). Por lo general, lo requerido para entrar a vacantes móviles (no solo aquí en WZ, sino ya prácticamente en cualquier empresa) son las siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fundamentos del SDK de iOS o de Android (según corresponda). Incluyendo life-cycle, uso de diversas herramientas dentro del framework, etc (en Android es el uso de fragmentos, activities, navigation components, estilos, etc).&lt;/li&gt;
&lt;li&gt;Patrones de Arquitectura como MVVM (recomendado) o MVP. MVC ya no se toma en cuenta.&lt;/li&gt;
&lt;li&gt;Manejo de arquitecturas como Clean Architecture o VIPER. &lt;/li&gt;
&lt;li&gt;Tener conocimiento de los principios de SOLID.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Estos conocimientos, te serán evaluados en dos entrevistas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entrevista de arquitectura de apps: Aquí pondrás a prueba tus skills con el manejo de datos provenientes ya sea de un modelo de datos o de una REST API. &lt;/li&gt;
&lt;li&gt;Entrevista de UI: Aquí tendrás que demostrar tus conocimientos en el uso de UI de la plataforma que has elegido implementando un layout de acuerdo a los requerimientos que te den los reclutadores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finalmente, se te hará una última entrevista con el líder de la división, quien en 30 minutos platicará contigo sobre ti y sobre lo que es la empresa. En esta entrevista es la última oportunidad que tienes para despejar todas tus dudas antes de que se tome la decisión final.&lt;/p&gt;

&lt;p&gt;Wizeline siempre te notificará si eres elegido o no. Si eres elegido, el/la reclutador(a) que comenzó tu proceso te notificará de ello y te enviará propuesta económica así como los siguientes pasos y la fecha de tu ingreso. &lt;/p&gt;

&lt;p&gt;En caso contrario, igualmente el/la reclutador(a) te enviará un correo con las áreas de oportunidad que has tenido para poder volver a postular en &lt;strong&gt;3 meses&lt;/strong&gt;. Como tal no hay un mínimo predeterminado, pero es lo recomendable para volver a aplicar.&lt;/p&gt;

&lt;h2&gt;
  
  
  A un mes de entrar...
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgryfucjp80pab7o0zska.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgryfucjp80pab7o0zska.png" alt="image" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Me convertí en Wizeliner el 2 de agosto pasado, por lo que al día de hoy ya llevo 1 mes laborando para esta gran empresa. Como tal no había visto que una organización tuviera ese balance que uno como colaborador de una empresa tenga: el balance entre el compromiso contraído y el de tener tu vida personal en orden. Haciendo que día a día no se vuelva tedioso e incluso dejes de pensar en la &lt;em&gt;llegada del lunes&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Dentro de WZ, he sentido la motivación que en algunas ocasiones ha faltado en trabajos anteriores y aunque llevo solamente un mes, estoy completamente seguro que podrán ser muchos meses más. Ya a partir de aquí todo depende de mi y de mantener la actitud como la tengo en este momento y aprovechar los recursos que la empresa proporciona para poder servir a la misma como debe ser. &lt;/p&gt;

&lt;h2&gt;
  
  
  Unos tips para las entrevistas
&lt;/h2&gt;

&lt;p&gt;Finalmente te daré 5 tips por si quieres postular:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Estudia bien sobre qué quieres entrar.&lt;/li&gt;
&lt;li&gt;Si vas a front o backend, estudia también algoritmos. Te recomiendo el libro &lt;em&gt;Cracking the Coding Interview&lt;/em&gt;, viejo lobo de mar en estas entrevistas y problemas.&lt;/li&gt;
&lt;li&gt;Siempre pregunta lo esencial sobre Wizeline, todas las dudas que tengas, despéjalas.&lt;/li&gt;
&lt;li&gt;Antes de las entrevistas técnicas, descansa y mantente bien hidratado y alimentado. Por lo general son 2 horas contínuas que estarás en tu computadora.&lt;/li&gt;
&lt;li&gt;Demuestra una &lt;strong&gt;gran actitud&lt;/strong&gt; en todas tus entrevistas. Eso también es importante.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pues bien, no me queda mas que agradecer al team que estuvo en mi proceso de reclutamiento y les mando un gran abrazo a todos y cada uno de ellos y espero que este post sirva para hacer crecer la empresa aún más. Si les interesa alguna vacante, pueden checarlas en este &lt;a href="https://www.wizeline.com/careers" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>recruitment</category>
      <category>spanish</category>
      <category>android</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Post Mortem: La ansiedad de una puesta en producción</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Sun, 13 Jun 2021 13:08:46 +0000</pubDate>
      <link>https://dev.to/rzerostern/post-mortem-la-ansiedad-de-una-puesta-en-produccion-3011</link>
      <guid>https://dev.to/rzerostern/post-mortem-la-ansiedad-de-una-puesta-en-produccion-3011</guid>
      <description>&lt;p&gt;Mis niños, el día de hoy, de hecho hace no más de 3 o 4 horas he puesto en producción una actualización a un sistema que se encarga de llevar entradas y salidas de estacionamientos. &lt;/p&gt;

&lt;p&gt;Sin embargo, como cualquier desarrollador, hay veces que los nervios se apoderan de uno y me genera ese bonito sentimiento que llamamos &lt;strong&gt;ANSIEDAD&lt;/strong&gt; y que desencadena aún más cosas y quisiera compartirlo con ustedes, amigos desarrolladores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i0Pid0Vp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/3ee302b821f984c8a98729230b2a9da6/tenor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i0Pid0Vp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/3ee302b821f984c8a98729230b2a9da6/tenor.gif" alt="Sylvester Anxious" width="400" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Según la bendita Wikipedia, la ansiedad es un &lt;em&gt;mecanismo de defensa natural del organismo frente a estímulos externos o internos que son percibidos por el individuo como amenazantes o peligrosos&lt;/em&gt;. En mi específico caso, puede llegar a darme en la torre con insomnio (de hecho escribo este post para poder conciliar el sueño) y el síndrome del impostor. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1s81u8yv9nmxv88vkps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1s81u8yv9nmxv88vkps.png" alt="Me da amzieda" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Por qué llega a pasarte eso?
&lt;/h2&gt;

&lt;p&gt;Pues bien, primeramente el que todo te reviente en las manos en un modelo de negocio bastante complejo o en donde el trabajo de reingeniería, &lt;em&gt;refactoring&lt;/em&gt;, planeación y demás ha sido extremadamente laborioso es algo que me causa pavor, puesto en algunas ocasiones en lo primero que pienso es:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;"Ya valió ma&lt;/strong&gt;&lt;strong&gt;, me van a correr/quitar el proyecto"&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si algo puedo decirte, es que en la mayoría de las ocasiones no será así. No es por querer decir que uno sea indispensable en el mismo, pero si ya llevas un tiempo considerable en el mismo, pues quiere decir que tu trabajo es el que ha hablado y con creces e inclusive te empiezas a hacer más prestigio en la empresa para la que trabajas (como empleado o freelance), sin embargo es ahí en donde entra el otro peor amigo: &lt;strong&gt;el síndrome del impostor&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¡Valgo versia otra vez!: ¿Qué es el síndrome del impostor?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rV2rShk---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/a02d568a4debce8de8283ced59e31bb9/tenor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rV2rShk---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/a02d568a4debce8de8283ced59e31bb9/tenor.gif" alt="Bad Dobby" width="404" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Igualmente, según Wikipedia, el síndrome del impostor es &lt;em&gt;un fenómeno psicológico en el que la gente se siente incapaz de internalizar sus logros y sufre un miedo persistente de ser descubierto como un fraude&lt;/em&gt;. Un ejemplo de cómo llega a manifestarse es el constante miedo a que un proceso que hiciste llegue a tener bugs o que lleguen a refactorizar tu código. &lt;/p&gt;

&lt;p&gt;Te lo digo como experiencia, en algunas ocasiones cuando un proceso me llega a salir mal, hay veces que me pasa por la cabeza la clásica frase &lt;em&gt;¿y si mejor me dedico a otra cosa?&lt;/em&gt; o también al ver el progreso de colegas más experimentados el decir &lt;em&gt;jamás voy a llegar a ser como ellos, no creo ser tan senior&lt;/em&gt;, cuando es de que en Github he podido hacer algoritmos bastante perrones e inclusive apps completas.&lt;/p&gt;

&lt;p&gt;Al final del día, termina pasando ese sentimiento y volvemos otra vez al mundo de la tecla como si nada.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cómo puedo mejorar y combatir a esa pareja malévola?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KKZqwwND--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/8beaa66c07b5bbea2be1b6a46acebdf5/tenor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KKZqwwND--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/8beaa66c07b5bbea2be1b6a46acebdf5/tenor.gif" alt="We're going to war" width="498" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si me animé a escribir esto, es precisamente para ayudar a quien lo desee y pues igualmente, en mi experiencia dividamos en dos el cómo mejorar estos aspectos: las habilidades blandas y las habilidades duras.&lt;/p&gt;

&lt;h3&gt;
  
  
  Habilidades Blandas
&lt;/h3&gt;

&lt;p&gt;En tus habilidades blandas te recomiendo lo siguiente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Trabaja en equipo: siempre asesórate con colegas o con cualquier persona que se encuentre en tu entorno. Si estás solo (comunmente con los freelancers), acude a cualquier comunidad de desarrolladores de software. En México hay varias y una de las que recomiendo es PHPMX (no solo se habla de PHP y siempre vas a encontrar a alguien que te ayude).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Platica tus problemas con gente de tu confianza: en quien tú confíes, platica estos malestares. Platica siempre cuando te sientas ansioso, cuando sientas que no van las cosas bien y aunque no sea del ramo, te desahogarás y podrás dejar atrás eso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manda a la chin**** a quienes hagan menos tu trabajo: No precisamente con groserías. Encuentra la manera de ser &lt;em&gt;polite&lt;/em&gt; y dale la vuelta a sus estupideces. En cuanto demuestres tu valía, creeme que el silencio es más ensordecedor que una mentada de madre.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No sobrepienses las cosas: Si llega a suceder algo, encuentra la solución y ya. No te concentres en lo que va a suceder después. Recuerda siempre que &lt;strong&gt;todo en la vida tiene solución, menos la muerte y un DELETE sin WHERE y sin respaldo&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Habilidades duras
&lt;/h3&gt;

&lt;p&gt;Ya en la parte de software engineering te puedo sugerir:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Haz pruebas: &lt;strong&gt;PRUEBA, PRUEBA, PRUEBA&lt;/strong&gt; antes de hacer deployment a producción. Igualmente entrena todo lo referente a Unit Testing, E2E Testing, Integration Testing, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Crea un plan de contingencia técnica: No dejes todo a la suerte. Siempre ten todo respaldado y procura que puedas tener un punto de rollback por si el servicio llega a caerse. Eso es primordial.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitorea: Ya que hayas puesto en producción, mantente al pendiente del desarrollo y ya si ves que todo está corriendo como debe de, ve por una cerveza (o algo de tu agrado) y disfruta.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Un consejo de pilón (pero muy importante)
&lt;/h2&gt;

&lt;p&gt;Finalmente y como un consejo especial, siempre pide a tu empleador o cliente autorización para que puedas poner tu participación en ese desarrollo dentro de tu portafolio. Al final del día, es lo que va a quedar para la posteridad. &lt;/p&gt;

&lt;p&gt;Si lo sacan de línea, POPOCATÉPETL. Nada es eterno y en algún momento un sistema tiene que morir. Solo guarda testigos del mismo para posteriores referencias (claro, si te dan la autorización).&lt;/p&gt;

&lt;p&gt;Pues bien, ya me está entrando el sueño y pues quiero echarme una siesta, pues este domingo me va a tocar monitorear esta bonita actualización.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>spanish</category>
      <category>leadership</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Post Mortem: El Pokedex en Android y Clean Architecture</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Wed, 24 Feb 2021 06:21:56 +0000</pubDate>
      <link>https://dev.to/rzerostern/post-mortem-el-pokedex-en-android-y-clean-architecture-2lc9</link>
      <guid>https://dev.to/rzerostern/post-mortem-el-pokedex-en-android-y-clean-architecture-2lc9</guid>
      <description>&lt;p&gt;Hey, ¡mis chamacos! Ya estoy de regreso deleitándolos con más contenido en español. En esta ocasión quiero contarles sobre un poquito más de desarrollo Android. Como ustedes sabrán, uno de los ejercicios que como ingenieros de software nosotros hacemos para pulir nuestras técnicas en el desarrollo de software es el desarrollo del popular &lt;strong&gt;POKEDEX&lt;/strong&gt;, el cual es la guía de todo maestro Pokémon y prácticamente su mano derecha.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué rayos es el Pokedex?
&lt;/h2&gt;

&lt;p&gt;Como ya lo mencioné, es la guía de un maestro Pokémon en forma de un dispositivo electrónico de formas diferentes, mismo que servía además de "enciclopedia" Pokémon, para otras cosas como mapa y comunicaciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué fue lo que se implementó?
&lt;/h2&gt;

&lt;p&gt;Pues bien, dentro de este desarrollo que se hizo con Kotlin 100%, se estuvieron utilizando diversas prácticas usadas hoy en día en el desarrollo móvil como la implementación de la conocida "Clean Architecture" y de la inyección de dependencias. &lt;/p&gt;

&lt;p&gt;Para ello, usamos Koin para la inyección de dependencias y fuimos armando la parte de datos en un módulo "core" separado de la parte de presentación de la app. En la parte de llamadas a la API también se utilizó un poco de RxJava/RxKotlin y RxAndroid.&lt;/p&gt;

&lt;p&gt;Luego para el backend, se implementó PokeAPI como backend, mismo que se ha convertido en un referente en el desarrollo de este proyecto en diferentes lenguajes, sobre todo en los que son usados en frontend (JS, Dart, TypeScript) y apps móviles (Swift, Kotlin, Java).&lt;/p&gt;

&lt;p&gt;Finalmente para las librerías externas, se utilizaron las habituales en un desarrollo de este tipo tales como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Glide&lt;/li&gt;
&lt;li&gt;Retrofit&lt;/li&gt;
&lt;li&gt;GSON (extensión de Retrofit)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sobre Clean Architecture
&lt;/h2&gt;

&lt;p&gt;Clean Architecture es un marco de trabajo propuesto por Robert C. Martin como forma de estructurar nuestro código en capas, mismas que solamente se pueden comunicar entre las que están a sus lados.&lt;/p&gt;

&lt;p&gt;El esquema de Clean Architecture es el siguiente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3vlcstar9ygb161e8ea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3vlcstar9ygb161e8ea.png" alt="image" width="772" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y las capas, tomando en cuenta Android son las siguientes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI&lt;/strong&gt;: La interfaz de usuario (el Fragment). Pero ¡AGUAS!, no quiere decir que vayas a meter toda la lógica de llamadas a datos en el Fragment (mala praxis), para ello se usa el ViewModel del mismo, que es donde viene la siguiente capa.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Presentación&lt;/strong&gt;: Son clases que se comunican con las clases e interfaces de interacción de nuestra aplicación realizando las llamadas pertinentes a datos. Como lo mencioné en el punto anterior, aquí es donde entran los ViewModel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Use Cases o Interactors&lt;/em&gt;&lt;/strong&gt;: Estas clases se encargan de interactuar con el modelo de negocio de datos e implementa el modelo de negocio pertinente para el manejo de datos dentro de nuestra aplicación. En este punto es donde uno como desarrollador puede armar diversos comportamientos avanzados con la información entregada por la capa contigua: los Repositorios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repositorios o &lt;strong&gt;&lt;em&gt;Entities&lt;/em&gt;&lt;/strong&gt;: Aquí es donde se albergan todas las llamadas a datos internos y externos de nuestra aplicación, es decir, las llamadas a REST API, servicios web diversos, bases de datos SQLite (si es que se requiriese ROOM), etc. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Caso Práctico
&lt;/h2&gt;

&lt;p&gt;Ahora bien, como un caso práctico les mostraré un flujo de datos usando CA. Una disculpa si no doy tanto detalle de la implementación y no toco un par de tópicos, pero me gustaría profundizar en ellos por separado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ReactiveX (tal vez me aviente algo sobre corrutinas).&lt;/li&gt;
&lt;li&gt;Inyección de Dependencias.&lt;/li&gt;
&lt;li&gt;El modelo MVVM utilizado en apps móviles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para fines de implementación se debe de realizar de forma inversa (de adentro hacia afuera) por lo que primeramente sería implementar nuestro llamado a Retrofit de esta forma:&lt;/p&gt;

&lt;p&gt;CAPA 4: REPOSITORIOS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;RestApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getRegions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RegionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region/{region}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getDetailedRegion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RegionDetailedResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokedex/{pokedex}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokedex"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokedexResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon/{pokemon}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon-species/{pokemon}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonSpeciesByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonSpeciesResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada uno de los métodos declarados en dicha interfaz serán los utilizados por nuestros repositorios y de esta forma es como los iremos implementando cada uno. Posteriormente, se realiza la implementación de dicha interfaz por medio de una clase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RestApiImp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;retrofit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;REQUEST_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;okHttpClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;

        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;okHttpClient&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;initOkHttpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retrofit&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;retrofit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MAIN_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;okHttpClient&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCallAdapterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RxJava2CallAdapterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GsonConverterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;retrofit&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;initOkHttpClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;REQUEST_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;REQUEST_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;REQUEST_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;interceptor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpLoggingInterceptor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpLoggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;interceptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpLoggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BODY&lt;/span&gt;
            &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInterceptor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;original&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;newRequest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proceed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;okHttpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como pueden ver, la clase cuenta con un "companion object", el cual es el símil en Kotlin de declarar una clase estática y en dicho objeto, tenemos el método getClient, mismo que se encargará de realizar la implementación de Retrofit, la extensión para GSON y un interceptor para inyectar cabeceras antes de llamar al request (es aquí donde podríamos inyectar tokens, sin embargo no las necesitamos en esta ocasión).&lt;/p&gt;

&lt;p&gt;Luego, pasamos a los repositorios persé. Para poder llamar a los métodos para obtener los diferentes tipos de "Pokedex", declaramos la siguiente interfaz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokedexRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokedexResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con su respectiva implementación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokedexRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;apiRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RestApi&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RestApiImp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RestApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokedexResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;apiRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En esta interfaz y clase, podemos nosotros ver que invocaremos como tal a Retrofit hacia un endpoint de los declarados en RestApi.kt pero como parte de la Clean Architecture, quien se encargará de ello serán los Interactors.&lt;/p&gt;

&lt;p&gt;CAPA 3: INTERACTORS&lt;/p&gt;

&lt;p&gt;Para los Interactors se declarará de la misma manera que los Repositorios, una interfaz y una clase de implementación, quedando de la siguiente forma la interfaz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokedexInteractor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokedexResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y la clase que implementa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexInteractorImp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokedexRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokedexRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemonRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PokedexInteractor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokedexInteractorImp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;simpleName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokedexResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pokedexRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doOnNext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemonEntries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;pokemonID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemonSpecies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MAIN_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon-species"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemonImage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Constant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;POKEMON_IMAGE_URL&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;pokemonID&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".png"&lt;/span&gt;
                        &lt;span class="n"&gt;pokemonRepositoryImp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemonSpecies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribeOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;response2&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemonDetails&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doOnComplete&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Service complete"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onErrorReturn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;null&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y como podemos observar en PokedexInteractorImp.kt, quien se encarga de armar lógicas complejas en el modelo de negocio de la aplicación es el interactor o use case. Es en esta capa donde se recomienda hacer las manipulaciones de datos pertinentes para que en la capa 2, únicamente se encargue de hacer las llamadas pertinentes para obtener estos datos.&lt;/p&gt;

&lt;p&gt;CAPA 2: PRESENTACIÓN&lt;/p&gt;

&lt;p&gt;En Android, la capa de presentación se ve representada por el ViewModel de nuestro fragmento en cuestión. Por lo tanto, dicho ViewModel tiene que estar establecido y asignado dentro del fragmento. Armamos el ViewModel como sigue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexInfoViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;compositeDisposable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CompositeDisposable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ApiDependencies&lt;/span&gt; &lt;span class="c1"&gt;// I'll talk about this later&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MutableList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;compositeDisposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokedex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribeOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemonEntries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;e&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto, nosotros vamos armando la capa 2 y dejamos todo dispuesto a que la capa 1 únicamente quede a la escucha de la información que llegue al momento de requerirlas y seguir el flujo de ida y vuelta conforme a la Clean Architecture.&lt;/p&gt;

&lt;p&gt;CAPA 1: UI&lt;/p&gt;

&lt;p&gt;Finalmente, el encargado de invocar todo este caminito será el Fragment (puedes usar Activities, pero gastarás más memoria), por lo que tenemos que realizarlo de la siguiente manera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexInfoFragment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Fragment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokedexInfoViewModel&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;toolbar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Toolbar&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ApiDependencies&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;recyclerView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RecyclerView&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setHasOptionsMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreateView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;inflater&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LayoutInflater&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewGroup&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inflater&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokedex_info_fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requireActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;AppCompatActivity&lt;/span&gt;
        &lt;span class="n"&gt;toolbar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findViewById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tb_pokedex_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setSupportActionBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toolbar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;supportActionBar&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokedex"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDefault&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"'s Pokedex"&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;supportActionBar&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDisplayHomeAsUpEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onOptionsItemSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;bundle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bundleOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="nc"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findNavController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requireView&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_pokedexInfoFragment_to_regionFragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onOptionsItemSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onActivityCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onActivityCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ViewModelProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokedexInfoViewModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewLifecycleOwner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Observer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;
            &lt;span class="nf"&gt;initRecyclerView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokedex"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;initRecyclerView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;listener&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OnClickListener&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bundle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bundleOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findViewById&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt_pokemon_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDefault&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="s"&gt;"pokedex"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokedex"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="nc"&gt;Navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findNavController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requireView&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action_pokedexInfoFragment_to_pokemonFragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;recyclerView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requireView&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;findViewById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rv_pokedex_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;recyclerView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHasFixedSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;recyclerView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layoutManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GridLayoutManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requireContext&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;recyclerView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;adapter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PokedexInfoAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onDetach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compositeDisposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onDetach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compositeDisposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Varios métodos son asignados como override pues son parte de la API de Android, sin embargo son de vital importancia (onDetach y onDestroy) para que el ViewModel no se quede en memoria al momento de cambiar de Fragment, salir de la app o algún punto en el que el ciclo de vida de la app se vea terminado o interrumpido.&lt;/p&gt;

&lt;p&gt;El método onActivityCreated es el encargado de realizar el llamado de todo el camino por medio del siguiente bloque:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewLifecycleOwner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Observer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;
            &lt;span class="nf"&gt;initRecyclerView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokedex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokedex"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al momento de asignar el ViewModel, nosotros asignamos un observer de LifeCycle (no confundir con los observers de ReactiveX) para que en cuanto la información llegue, realice los procesos que uno asigne dentro del Observer y así poder enviar la información hacia la vista del Fragment (nuestro XML).&lt;/p&gt;

&lt;h2&gt;
  
  
  Siguientes pasos
&lt;/h2&gt;

&lt;p&gt;Hasta ahorita ando viendo el trabajar lo siguiente ya que se tiene una versión estable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mejorar el UX/UI de la app. Ahorita lo hice muy básico.&lt;/li&gt;
&lt;li&gt;Agregar un news feed en el dashboard. Ahorita no lo agregué porque no encontré ninguno libre.&lt;/li&gt;
&lt;li&gt;En lugar de usar RxKotlin, poder cambiar a &lt;strong&gt;corrutinas&lt;/strong&gt; para la parte asíncrona y tener una alternativa de trabajar las llamadas de este tipo. Aunque la verdad, soy más fan de ReactiveX que de las corrutinas, pero uno nunca sabe cuándo necesitará trabajar con ellas.&lt;/li&gt;
&lt;li&gt;Revisar que no haya memory leaks en la app.&lt;/li&gt;
&lt;li&gt;CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como es costumbre, he de comentar que no soy el mejor desarrollador de Android y que tal vez este código no sea el mejor que hayas visto y me gustaría mejorarlo. Si te interesa dejar tus issues, darle stars, forkearlo o ayudarme a mejorar, puedes hacerlo en el siguiente repositorio: &lt;a href="https://github.com/RZEROSTERN/roadtocertification_pokedex" rel="noopener noreferrer"&gt;https://github.com/RZEROSTERN/roadtocertification_pokedex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Igualmente, si quieren dejar sus comentarios, pueden hacerlo bajo este post. Estaré feliz de contestarlos.&lt;/p&gt;

&lt;p&gt;Bueno pues, espero que les haya gustado este post, aunque largo, nutritivo en cuestiones que tal vez puedan sacar de dudas a más de una persona.&lt;/p&gt;

&lt;p&gt;Happy coding !!!&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>mobile</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Post Mortem: Global Game Jam 2021</title>
      <dc:creator>Marco Ramírez</dc:creator>
      <pubDate>Wed, 03 Feb 2021 08:31:03 +0000</pubDate>
      <link>https://dev.to/rzerostern/post-mortem-global-game-jam-2021-5aha</link>
      <guid>https://dev.to/rzerostern/post-mortem-global-game-jam-2021-5aha</guid>
      <description>&lt;p&gt;Pues bien, el día de hoy les traigo otro post y en esta ocasión no les hablaré de aplicaciones móviles, sino de desarrollo de videojuegos. &lt;/p&gt;

&lt;p&gt;Una de mis más grandes pasiones son los videojuegos y me gusta mucho estar inmiscuido en dicho mundo, desde "manquear" un ratito en la PC o en el Nintendo Switch hasta el desarrollo pasando por un poco de armado de arte y demás.&lt;/p&gt;

&lt;p&gt;Ahora bien, me gustaría dar un &lt;em&gt;post mortem&lt;/em&gt; sobre mi participación en la Global Game Jam 2021, misma que comenzó el 27 de enero y terminó el día 31 todo de forma remota pues el COVID19 está haciendo de las suyas y no nos deja salir para nada.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿QUÉ ES LA GLOBAL GAME JAM?
&lt;/h2&gt;

&lt;p&gt;Pues bien, la Global Game Jam es un evento &lt;strong&gt;anual&lt;/strong&gt; de desarrollo de videojuegos en el que prácticamente cualquier persona interesada en hacer un videojuego, juego de rol o juego de mesa puede participar sin importar su experiencia. Las reglas son muy sencillas: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be kind&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No digas el tema más que a tus colegas dentro de la sede.&lt;/strong&gt; Ni siquiera en redes sociales. Esto solo lo puedes hacer hasta que Honolulu haya empezado el evento (de verdad).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;De ahí, tienes 48 horas para armar un prototipo de juego y presentarlo a los demás. Puedes valerte de cualquier herramienta y prácticamente si necesitas comprar/descargar assets, lo puedes hacer dando crédito a sus creadores (sí, tampoco hay que ser piojos).&lt;/p&gt;

&lt;h2&gt;
  
  
  GGJ2021 en tiempos de COVID19
&lt;/h2&gt;

&lt;p&gt;Como todo, la Global Game Jam se tuvo que adaptar a realizarse en un formato digital. Mismo que fue muy cómodo para muchísimos de nosotros, pues el traslado a las diferentes sedes puede ser algo caótico (desde donde vivo a la sede del CCD en CDMX son 2 horas de camino :( ) y nos puso en contacto con muchísimas personas que comparten el mismo interés por los videojuegos.&lt;/p&gt;

&lt;p&gt;Esta facilidad me dio a mi la oportunidad de participar en tres proyectos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PSYCHE (de este hablaré en el presente Post Mortem)&lt;/li&gt;
&lt;li&gt;De lo perdido... lo encontrado (un pequeño juego de rol, iniciándome en el desarrollo de juegos de este género)&lt;/li&gt;
&lt;li&gt;Codename Panopticon. Este lo hice con un amigo de San Diego, CA. Aunque apenas va el concepto, espero que desee continuar con el mismo y no me mande a volar :(.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Afortunadamente, para los 3 hubo tiempo, además de mis pendientes en el trabajo y pude retomar mi camino de disciplina que perdí a causa del Cobijas (sí, me dio COVID19 por segunda ocasión). Pero es tiempo de contarles cómo me fue.&lt;/p&gt;

&lt;h2&gt;
  
  
  PSYCHE: El inicio de una IP
&lt;/h2&gt;

&lt;p&gt;Primeramente les daré el bosquejo narrativo del videojuego y ya después el cómo me fue :3.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿De qué va el juego?
&lt;/h3&gt;

&lt;p&gt;Psyche es un juego que tiene como lugar la CDMX y trata de Adrián, un chico como cualquiera que se dedica a programar y a estar apegado al trabajo todo el tiempo. A pesar de ello tiene a su novia, Ivette y en ella ve un apoyo y a una gran persona con la que piensa vivir por el resto de sus días.&lt;/p&gt;

&lt;p&gt;Desafortunadamente, un día despierta con un mensaje de Whatsapp que dice más o menos así:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hola Adrián:&lt;/p&gt;

&lt;p&gt;Quiero escribirte para terminar nuestra relación. Tú no tienes la culpa, eres una gran persona, sin embargo nuestros caminos deben separarse.&lt;/p&gt;

&lt;p&gt;Por favor, no me busques.&lt;/p&gt;

&lt;p&gt;Ivette&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Al ver el mensaje, Adrián entra en depresión y busca respuestas, pues no era posible que algo que había sido tan bonito a la vez fuera tan pasajero. Entonces decide ir a una sesión de terapia psicológica para saber qué fue lo que pasó, sin saber él que con ello iba a cambiar la perspectiva que tiene de Ivette, él mismo, su psique e inclusive de eventos del pasado que creyó olvidados.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo me fue con el desarrollo?
&lt;/h3&gt;

&lt;p&gt;Pues al empezar el desarrollo y en base a que ya tenía muchas cosas en la narrativa que considero que estaban listas a espera de pulirlas, decidí poner manos a la obra el día 26. Ese día dieron el tema de este año a nivel internacional: &lt;strong&gt;LOST AND FOUND&lt;/strong&gt;. Al ver que coincidía el tema con la narrativa del juego, fue tiempo de codear. Entonces, agarré Unity y unos asset packs que había comprado previamente y empecé a armar las diferentes escenas que involucraban al juego. &lt;/p&gt;

&lt;p&gt;Me dí cuenta de varias cosas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unity da tratamiento diferente en la interfaz a la parte de la UI y al Tilemap y al principio te malviajas, pero ya al final le agarras la onda y listo.&lt;/li&gt;
&lt;li&gt;Por fin le agarré el hilo a las herramientas de Tilemaps y me facilité el trabajo a lo bestia.&lt;/li&gt;
&lt;li&gt;Igualmente investigué del uso de librerías que ya son estándar en el desarrollo con Unity como Cinemachine y una de IA básica.&lt;/li&gt;
&lt;li&gt;También pude realizar deploys tanto a Windows como a MacOS, aunque solo publiqué en Windows por tiempo, con esto pude confirmar el multi-platform para computadoras. Incluso se podría hacer deploy para XBOX One, pero pues como no tengo, me la pelé :(.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En cuestiones de código fue más sencillo, puesto que al tener ya conocimientos previos de C# y estar con el diplomado de desarrollo videojuegos de la Universidad Anáhuac, así como el ver los videos de Brackeys, es como pude sacar adelante el juego, que a pesar de no estar completo, sí quedó muy avanzado en los aspectos básicos.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Y qué sigue?
&lt;/h2&gt;

&lt;p&gt;Me encantó trabajar por fin en un proyecto de videojuegos después de más de 5 años de no haber hecho mucho y pues la verdad estoy muy ansioso de seguir trabajando en él. De principio de cuentas, hay que hacer lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agregar shooting al personaje principal&lt;/li&gt;
&lt;li&gt;Finalizar los personajes (protagonistas, antagonistas y minions).&lt;/li&gt;
&lt;li&gt;Arreglar bugs y sacar el primer alpha.&lt;/li&gt;
&lt;li&gt;El website.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Después para irme al siguiente Alpha, considero lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminar los tres primeros escenarios.&lt;/li&gt;
&lt;li&gt;Probar un reproductor de videos cinemáticos en Unity. Ya debe haber alguno hecho y está en la Asset Store o de forma gratuita.&lt;/li&gt;
&lt;li&gt;Mejorar la IA de los enemigos.&lt;/li&gt;
&lt;li&gt;Mecanismo de seguimiento para los protagonistas (Una protagonista adicional seguirá al principal, hay que detallarlo más).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;De pendientes opcionales están:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Armar assets originales y prescindir de los que usé. No es porque sean malos, de hecho son buenísimos, sin embargo, me gustaría hacer algo más original. Esta es tentativa, pues los assets que usé fueron comprados y están autorizados para desarrollos comerciales.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finalmente, les dejo el video de "trailer" que armé ese día, como demostración de lo que hice en ese fin de semana, esperando que sea de su agrado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=cxyWgMCx-do" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bqJBeFgH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.youtube.com/vi/cxyWgMCx-do/0.jpg" alt="PSYCHE TEASER TRAILER" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Próximamente les traeré más aventuras de código, mientras tanto, solo puedo decirles como de costumbre Happy coding!&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>globalgamejam</category>
      <category>spanish</category>
      <category>unity3d</category>
    </item>
  </channel>
</rss>
