<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jander Laffita Orduñez on Medium]]></title>
        <description><![CDATA[Stories by Jander Laffita Orduñez on Medium]]></description>
        <link>https://medium.com/@janderlaffita?source=rss-9bb141b7ac54------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*DGxTGvOHw9L-V0YyoUK7PQ.jpeg</url>
            <title>Stories by Jander Laffita Orduñez on Medium</title>
            <link>https://medium.com/@janderlaffita?source=rss-9bb141b7ac54------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 02:25:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@janderlaffita/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[¿Como crear un manejo de errores al estilo de Kotlin con Flutter?]]></title>
            <link>https://medium.com/@janderlaffita/como-crear-un-manejo-de-errores-al-estilo-de-kotlin-con-flutter-aaceab9ea87f?source=rss-9bb141b7ac54------2</link>
            <guid isPermaLink="false">https://medium.com/p/aaceab9ea87f</guid>
            <category><![CDATA[error-handling]]></category>
            <category><![CDATA[dios]]></category>
            <category><![CDATA[sealed-classes]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Jander Laffita Orduñez]]></dc:creator>
            <pubDate>Sat, 19 Aug 2023 22:27:59 GMT</pubDate>
            <atom:updated>2023-08-20T02:36:02.597Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*woq5SMHve4wf0mdtmFGQGw.jpeg" /></figure><p>Hola a todos hace ya tiempo no escribo artículos para Medium por buena causa. He tenido que aprender sobre el desarrollo multiplataforma con Flutter por motivos laborales <em>(demanda creciente de flutter devs en Cuba)</em>. Cualquier tecnología que escojamos para la creación de apps frontend casi seguro que vamos a tener que consumir alguna API y Flutter no es la excepción. Hoy no vamos a ver como consumir un API, sino como bien anuncia el titulo, vamos a tratar sobre el <strong>error handling</strong>.</p><p>Uno de los principios que más me ha costado adaptar a Flutter desde nativo, es este, y es que la comunidad de devs de Flutter por limitaciones del propio lenguaje ha tomado varios caminos al momento de crear un manejo adecuado de errores. En mi búsqueda de la solución mas parecida a lo que hago en nativo con Kotlin me topé con todo. Desde el simple try-catch hasta soluciones mas complejas como el uso de programación funcional <em>(generalmente con el uso de </em><a href="https://pub.dev/packages/fpdart"><em>fpdart package</em></a><em> )</em>. Gracias a Dart 3.0 y la inclusión de las <strong>sealed class</strong> obtuve la solución adecuada para mí,<br>un manejo de errores al estilo de Kotlin.</p><p><strong>Manos a la obra.</strong></p><p>Lo primero es crearnos una <em>sealed class</em> que represente una respuesta del API, tendríamos los 3 estados que tenemos en una petición a un API (<strong>Loading, Success y Failure</strong>).</p><pre>sealed class Result&lt;S, E extends Exception&gt; {<br>  const Result();<br>}<br>final class Loading&lt;S, E extends Exception&gt; extends Result&lt;S, E&gt;{}<br><br>final class Success&lt;S, E extends Exception&gt; extends Result&lt;S, E&gt; {<br>  const Success(this.value);<br>  final S value;<br>}<br><br>final class Failure&lt;S, E extends Exception&gt; extends Result&lt;S, E&gt; {<br>  const Failure(this.exception);<br>  final E exception;<br>}</pre><p>Esta clase hace uso de genéricos, <strong><em>S </em></strong>representa el tipo de retorno satisfactorio y <strong><em>E </em></strong>representa el tipo de retorno de Failure ,que hereda de la clase <em>Exception </em>y así limitamos que solo puedan ser usadas <strong>exceptions</strong>.</p><p>Podemos explotar más nuestro dominio y crear clases que representen los <br>errores más comunes que puede lanzar un API. Tendríamos algo como esto.</p><pre>class AppException implements Exception{<br>  final String message;<br>  final int statusCode;<br><br>  AppException(this.message, this.statusCode);<br><br>  static AppException fromStatusCode(int statusCode){<br>    switch(statusCode){<br>      case 400: return BadRequest();<br>      case 401: return UnAuthenticated();<br>      case 404: return NotFound();<br>      default: return UnKnown();<br>    }<br>  }<br>}<br><br>class UnAuthenticated extends AppException{<br>  UnAuthenticated({String message = &#39;no authenticated&#39;}): super(message, 401);<br>}<br><br>class BadRequest extends AppException{<br>  BadRequest({String message = &#39;bad request&#39;}): super(message, 400);<br>}<br><br>class NotFound extends AppException{<br>  NotFound({String message = &#39;not found&#39;}): super(message, 404);<br>}<br><br>class UnKnown extends AppException{<br>  UnKnown({String message = &#39;unknown error&#39;}): super(message, 0 );<br>}<br><br>class NoConnection extends AppException {<br>  NoConnection({String message = &#39;no internet connection&#39;}): super(message,-1);<br>}</pre><p>Como el código suele ser más claro que las palabras muchas veces, seré breve. Tenemos <strong>exceptions </strong>personalizadas que representan errores comunes como (<em>unAuthenticated, Bad Response, Not Found, No Connection…</em>) y un método que nos va a retornar una <strong>Exception</strong> según su status code(que reconozco podría convertirse en algo molesto de escalar, pero para este ejemplo esta bien).</p><p>Ahora podemos codificar un <strong>datasource</strong> como el siguiente. Este nos retorna nuestra <em>selead class</em> <strong>Result</strong>. Por tanto si la petición falla tendremos un <strong>Failure() </strong>como respuesta y si es satisfactoria tendremos un <strong>Success()</strong></p><pre>class MovieDbDatasource extends RemoteMovieDatasource {<br>  MovieDbDatasource(this._api);<br><br>  final RestClient _api;<br><br>  @override<br>  Future&lt;Result&lt;Movie,AppException&gt;&gt; getMovieById(int movieId) async{<br>    try {<br>      final movieDb = await _api.getResponse(movieId);<br>      final movie = MovieMapper.toDomain(movieDb);<br>      return Success(movie);<br>    } catch (e) {<br>      final dioException = (e as DioException);<br>      return dioException.handlingError&lt;Movie&gt;();<br>    }<br>  }<br><br>  @override<br>  Future&lt;Result&lt;List&lt;Movie&gt;,AppException&gt;&gt; getPopularMovies({int page = 1})async {<br>    try {<br>      final response = await _api.getPopularMovies(page: page);<br>      final listOfMovie = response.results?.map((movieDb) =&gt; MovieMapper.toDomain(movieDb)).toList() ?? [];<br>      return Success(listOfMovie);<br>    } on Exception catch (e) {<br>      final dioException = (e as DioException);<br>      return dioException.handlingError&lt;List&lt;Movie&gt;&gt;();<br>    }<br>  }<br>}</pre><p>El método <em>handlingError</em> es una extensión de <strong>DioException </strong>que luce así.</p><pre>extension DioExceptionExtension on DioException{<br>  Failure&lt;S,AppException&gt; handlingError&lt;S&gt;(){<br><br>    if (error.runtimeType == SocketException) {<br>      return Failure(NoConnection());<br>    }<br>    final appException = AppException.fromStatusCode(response?.statusCode?? 0);<br>    return Failure(appException);<br>  }<br>}</pre><p>En fin nos retorna el <strong>Failure</strong> correspondiente a nuestro <strong>status code</strong>. Cero (0) es el valor por defecto si no tenemos <strong>status code</strong>, este corresponde a nuestra <strong>UnKown()</strong> <em>exception.</em></p><p>Ahora viene la magia y por lo que nos es útil el uso de las <strong>sealed class</strong>. El siguiente es un método dentro del <em>viewModel, controller o bloc </em>como lo quieras llamar, de acuerdo a tu Arch o gestor de estados favorito.</p><pre>Future&lt;void&gt; loadMovie()async {<br>    final result = await _getMovieById(movieId);<br>    switch(result){<br>      case Loading(): emit(state.copyWith(isLoading: true));<br>      case Success(): emit(state.copyWith(isLoading: false, movie: result.value , error: null));<br>      case Failure(): emit(state.copyWith(error: result.exception, isLoading: false));<br>    }<br>  }</pre><p>Ya que <strong>result</strong> es una <em>sealed class</em> tenemos un switch con ramas exhaustivas. Quizá podías haber ideado una solución donde switch retorne un valor de estado y luego sea emitido, es válido también. En tu <strong>vista </strong>es solo consumir este estado, que en caso de ser un <strong>Failure</strong> ya tenemos un<strong> status code</strong> y un <strong>message</strong> acorde al tipo de error y así poder dar un feedback adecuado al usuario.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*sbuDL0pr6iFHu-O3dRzQew.gif" /></figure><h4>Conclusión</h4><p>Podemos crear un manejo de errores al puro estilo de <strong>Kotlin,</strong> que tanto me gusta, gracias a las <em>sealed class</em> de Dart y poder asi dar un feedback adecuado, según el error lanzado por la request a nuestra API. Destacar que no siempre es útil el uso de este tipo de solución y con un simple try-catch tenemos, ya que en casos de uso más complejos podemos llegar a crear un código muy ilegible. <strong>Gracias </strong>espero le halla ayudado este post.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=aaceab9ea87f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Arrays Multidimensionales]]></title>
            <link>https://medium.com/@janderlaffita/arrays-multidimensionales-2f2dcae0b021?source=rss-9bb141b7ac54------2</link>
            <guid isPermaLink="false">https://medium.com/p/2f2dcae0b021</guid>
            <category><![CDATA[multidimensional-arrays]]></category>
            <category><![CDATA[arrays]]></category>
            <category><![CDATA[algorithms]]></category>
            <dc:creator><![CDATA[Jander Laffita Orduñez]]></dc:creator>
            <pubDate>Mon, 20 Mar 2023 18:00:09 GMT</pubDate>
            <atom:updated>2023-03-20T18:00:09.235Z</atom:updated>
            <content:encoded><![CDATA[<h3>Array Multidimensionales</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1013/1*81xPSVjZsv206w4Jf2_7Og.png" /></figure><p>Hola otra vez , soy yo <strong>Jander,</strong> tu programador favorito <strong>:)</strong></p><p>Estructurar los datos es una de las tareas fundamentales y básicas a la hora de programar. Pasa que siempre vamos a tratar con datos en los problemas cotidianos de la programación, más vale tenerlos organizados. Lo primero que nos topamos cuando empezamos a conocer estructuras de datos son los arreglos o vectores. Un vector no es más que un conjunto de datos del mismo tipo al cual podemos acceder a través de un índice que representa su posición <em>(Podemos crear arreglos de tipos de datos diferentes pero estos son menos usados )</em>. Cuando tenemos una agrupación dígase un conjunto de nombres de estudiantes:</p><p><strong><em>nombres = [“Samuel”,”Juan”,Pedro”,”Miguel”,”Alejandro”]</em></strong></p><p>relacionados por un único aspecto en este caso nombres de estudiantes <em>(Es un aula pequeña :))</em> nos encontramos ante un vector de una única dimensión <strong>unidimensional</strong>. Estos son sencillos porque para acceder a ellos ya sea para leer, escribir o actualizar es tan sencillo como conocer su <br>índice por ejemplo <em>“Samuel”</em> corresponde al índice 0<em> (la mayoría de lenguajes inician los índices desde 0)</em>.<strong> samuel = nombres[0]</strong>. Hasta aquí todo bien hasta que todo se empieza a enredar con el multiverso. Si, leíste bien, tenemos multiverso en la programación.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n2wwVG5aBw21nZ9p5-txEQ.jpeg" /></figure><p>Los arreglos multidimensionales suelen ser un dolor de cabeza para los que inician en la programación, al menos para mí lo fue. Cuando comencé a estudiarlos me los aprendí de una manera rígida y poco aplicable a la práctica. Hoy te voy a enseñar una manera en la que <strong>más nunca se olvidarán y comenzarás a aplicarlos a la práctica </strong>y resolver problemas con ellos. Vamos a ver los casos más comunes</p><h4>¿Que son los arreglos bidimensionales?</h4><p>Son sencillamente una tabla de filas y columnas. Supongamos que ahora queremos relacionar este listado de estudiantes que tenemos con las notas que han adquirido en las materias Matemáticas, Español, Historia</p><p><strong>materias = [Matemáticas, Español, Historia]</strong></p><p>Cada estudiante tendría ahora asignado un array de 3 índices que representan las notas en cada asignatura. Tendríamos una tabla donde cada <em>columna representa un estudiante</em> y cada <br><em>fila una asignatura</em> <em>o materia</em> cada <em>celda entonces representaría la nota de un estudiante</em> en determinada asignatura.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/585/1*psKqY1Dwksz0WTqQQVMhmw.png" /></figure><p>Dentro de cada estudiante hay un arreglo de notas, entonces, si queremos conocer la nota de Pedro en Español solo es saber que Pedro es<strong> nombres[3]</strong> y Español es<strong> materias[1]</strong><br>si esta tabla o matriz le diéramos el nombre de <strong>alumnosRelacion</strong>, sería tan sencillo como indicarle fila y columna (<strong>alumnosRelacion[3][1]</strong>). Pero qué pasa si queremos crear otra dimensión a esta tabla y añadir el parámetro de curso. Supongamos que esta es una escuela secundaria y queremos registrar cada año de secundaria 1º, 2º y 3º cada asignatura tiene tres años diferentes en las que se imparte. Sencillamente es agregar otro arreglo dentro de cada materia. Tendríamos una tabla como esta:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*VbVvxuuT7L2k-imWZKNp8w.png" /></figure><p>La estructura que tenemos es algo común en programación que son los <strong>listados anidados</strong>, al final esto son los arreglos: un listado. Array&lt;Array&lt;Array&lt;Type&gt;&gt;&gt;&gt;. Para acceder al valor de la nota de un estudiante es determinada asignatura en determinado año sería: <strong>matrizMultidimensional[3][1][2]</strong> , en nuestro ejemplo es Miguel en Español en el 3º año de secundaria que son <strong>4 puntos</strong>.</p><p>Imagínate la magia que podemos hacer con esto en programación <strong>:-)</strong></p><p>Pero como entiendo todo esto? La forma que te enseñaré es algo propio, una conclusión a la que llegue yo mismo, no la he visto en otro sitio pero puede que ya se haya explicado de esta forma. También puede que no sea la forma más correcta de explicar pero así fue que la entendí.</p><p>1- Primero necesitamos un tipo de dato único que gestionar, al final todo este embrollo se trata de lograr un manejo de datos no? Puede ser primario dígase<em> carácter, entero, double, buleano</em> o un objeto de una clase creada por nosotros mismos, en nuestro ejemplo el dato es un entero , la nota.</p><p>2- La cantidad de dimensiones depende de los parámetros que queremos relacionar, podemos pensar en decenas de ellos para aplicarlos al dato, en nuestro caso: <em>la nota:</em></p><ul><li>el estudiante que la obtuvo</li><li>la asignatura</li><li>el profesor que la imparte</li><li>el curso</li><li>la escuela</li><li>el tipo de escala (5/5 10/10 100/100)</li><li>la clave que se usó para otorgarla</li><li>etc...</li></ul><p>Pero para poder crear las dimensiones de una manera correcta estos parámetros deben tener una relación jerárquica entre sí de <strong>uno es a muchos</strong>, un tema que extraje a medias de las bases de datos relacionales. Te explico mejor:</p><p>Una jerarquía son niveles donde cada elemento está más arriba/afuera o más abajo/adentro que otro. La relación uno es a muchos es cuando el 1º elemento tiene muchos o varios de los 2º elementos. Ejemplo un estudiante cursa muchas asignaturas, cada asignatura tiene muchos años en<br>los que se imparte. Si quieres seguir continuar con la jerarquía pero usualmente no hay tantos niveles. Tenemos la jerarquía creada y el tipo de dato que queremos manejar, es solo unir las piezas: <strong>Un array que representa los estudiantes</strong> contiene <strong>un array q representa las asignaturas</strong> que a su vez contiene <strong>un array que representa los años</strong>, cada celda formada por estas dimensiones contiene nuestro tipo de dato<br><strong>Array&lt;Array&lt;Array&lt;Int&gt;&gt;&gt;&gt;.</strong></p><p>Tanto para asignar valores a todas estas casillas q se han creado como para recuperarlos podemos hacerlo manualmente, XD eso sería infernal, en nuestro caso 5 x 3 x 3 veces = 45 líneas de código repetitivas. O podemos usar bucles anidados, uno por cada dimensión. Si ya sé, eso es muy costoso a nivel se recursos pero no hay de otra. Por eso debemos<br>usar estas estructuras solo cuando se necesiten realmente</p><p>¿Como se ve esto en código ?</p><pre>// Creando el array multidimensional y estableciendo el valor<br>// de cada celda a 0<br>val multidimensionalArray : Array&lt;Array&lt;Array&lt;Int&gt;&gt;&gt; =<br> Array(5){ Array(5){Array(3){0} } }<br><br>// leer un valor de manera individual <br>val value = multidimensionalArray[1][2][3]<br><br>// iterando por cada celda <br><br>for (i in 0..4){<br>        for (j in 0..2){<br>            for (k in 0..2){<br>                val celda = multidimensionalArray[i][j][k]<br>                  // leer, escribir o cualquier otra lógica aplicada<br>                  // a la celda<br>            }<br>        }<br>    }</pre><p>Bueno esto es el primer paso, pero si entendiste la lógica de la estructura de datos puedes resolver problemas complejos usándola. Espero también que lo hallas entendido explicado de esta forma, así que cuando tengas que manejar datos donde tengas que relacionarlos a parámetros que identifiques que los puedas ordenar en una jerarquía con una relación entre ellos de uno a muchos <strong>(1:N) </strong>piensa que una de las soluciones podría ser usando arreglos multidimensionales.</p><p>Chao y hasta proto.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2f2dcae0b021" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Uso de MockWebServer para testear la capa de Network]]></title>
            <link>https://medium.com/@janderlaffita/uso-de-mockwebserver-para-testear-la-capa-de-network-c275db1a7e5c?source=rss-9bb141b7ac54------2</link>
            <guid isPermaLink="false">https://medium.com/p/c275db1a7e5c</guid>
            <category><![CDATA[mockwebserver]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-testing]]></category>
            <dc:creator><![CDATA[Jander Laffita Orduñez]]></dc:creator>
            <pubDate>Fri, 10 Mar 2023 21:35:35 GMT</pubDate>
            <atom:updated>2023-03-10T21:35:35.343Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/764/1*YMi5SC9JwFh62apBXODlaA.png" /></figure><p>Hola soy yo nuevamente <strong>Jander</strong> tu programador favorito : ). Sé que prometí la segunda parte de los testing unitarios, pero he tenido q mirar este tema recientemente y decidí hacer un post del mismo. Sin más muela (“charla”) como decimos en buen cubano, empecemos.</p><p>MockWebServer es una herramienta que nos ayuda a probar cómo nuestra aplicación Android interactúa con las APIs que consume. Con MockWebServer podemos crear un servidor falso que intercepta las peticiones HTTP que hacemos desde nuestra app y devuelve las respuestas que nosotros queramos. De esta forma, podemos verificar si nuestra app se comporta correctamente ante diferentes situaciones, como por ejemplo, errores del servidor, datos inválidos o tiempos de espera. Además, al usar MockWebServer no necesitamos tener una conexión real con un servidor externo, lo que hace nuestras pruebas más rápidas y fiables. Puedes usarlo como cualquier otro mock y simular la respuesta del servidor.</p><p>Esta respuesta la almacenamos en el directorio de <strong>androidTest</strong>, en un nuevo archivo q vamos a crear llamado networkresponses.json que se recomienda tenga la siguiente ruta <strong>src/debug/assets/. </strong>El mismo representa la response del servidor, (nos lo podemos crear o podemos usar una respuesta real del servidor(copy/paste))</p><p><em>Ejemplo:</em></p><pre>{<br>    &quot;marcadores&quot;: [<br>      {<br>        &quot;latitude&quot;: 40.416875,<br>        &quot;longitude&quot;: -3.703308,<br>        &quot;city&quot;: &quot;Madrid&quot;,<br>        &quot;description&quot;: &quot;Puerta del Sol&quot;<br>      },<br>      {<br>        &quot;latitude&quot;: 40.417438,<br>        &quot;longitude&quot;: -3.693363,<br>        &quot;city&quot;: &quot;Madrid&quot;,<br>        &quot;description&quot;: &quot;Paseo del Prado&quot;<br>      },<br>      {<br>        &quot;latitude&quot;: 40.407015,<br>        &quot;longitude&quot;: -3.691163,<br>        &quot;city&quot;: &quot;Madrid&quot;,<br>        &quot;description&quot;: &quot;Estación de Atocha&quot;<br>      }<br>    ]<br>}</pre><p><em>Este código representa la respuesta de un servidor en formato JSON. El json debe estar en correspondencia a la lógica de nuestra app. En este caso se trata de una lista de localizaciones</em></p><p>Para acceder al archivo debes configurar el build.grdle del módulo y sincronizarlo:</p><pre>testOptions {<br>  unitTests {<br>  includeAndroidResources = true<br>  }<br>}</pre><p>De esta forma ahora tus test unitarios pueden acceder a todos los recursos. El próximo paso es poder leer este archivo.json que contiene la respuesta del servidor y recuperarlo como un String. Te puedes crear un objeto como el siguiente:</p><pre>object JsonReader {<br>  fun getJson(path: String): String {<br>    return try {<br>      val context = InstrumentationRegistry.getInstrumentation().context<br>      val jsonStream: InputStream = context.assets.open(path)<br>      String(jsonStream.readBytes())<br>    } catch (exception: IOException) {<br>      Logger.e(exception, &quot;Error reading network response json asset&quot;)<br>      throw exception<br>    }<br>  }<br>}</pre><h4>¿Como crear nuestro test con el servidor mockeado?</h4><p><strong>1- Configuremos nuestra clase para los test donde mockearemos el server</strong></p><p>Anotar la clase test con <a href="http://twitter.com/RunWith">@RunWith</a>(RobolectricTestRunner::Class) te permitira usar el framework de android sin tener que lanzar un emulador gracias a la librería de Robolectric</p><p><strong>2- Crear la dependencia de MockWebServer y configurarla</strong><br> Inicia el servidor en el <em>puerto 8080</em> antes del test y apágalo luego que finalice el test</p><pre>private lateinit var mockWebServer: MockWebServer<br><br>@Before()<br>fun setup(){<br> mockWebServer = MockWebServer()<br> mockWebServer.start(8080)<br><br>}<br><br>@After<br>fun teardown() {<br>mockWebServer.shutdown()<br>}</pre><p><strong>3- Configurar la response del server</strong></p><p>Para ello tenemos la propiedad <strong>MockWebServer.dispatcher</strong> que podemos configurar con un objeto de la Clase Dispatcher que nos provee la librería de MockWebServer.<br>Por ejemplo en este trozo de código estamos configurando el dispatcher para recibir una respuesta mockeada con código 200 cuando reciba determinado endpoint</p><pre>...<br> private val endpointPath = &quot;/endpoinpath&quot;<br>...<br>private fun getDispatcherForValidToken() = object : Dispatcher() {<br>    override fun dispatch(request: RecordedRequest): MockResponse {<br>      return when (request.path) {<br>        endpointPath -&gt; { MockResponse().setResponseCode(200) }<br>        else -&gt; { MockResponse().setResponseCode(404) }<br>      }<br>    }<br>  }</pre><p>En este próximo ejemplo estamos configurando la respuesta, pero esta vez estamos añadiendo un Body apoyándonos en el objeto q habíamos creado previamente que nos devuelve un json en forma de String desde el archivo <strong>src/debug/assets/networkresponses.json </strong>en nuestros recursos</p><pre>private fun getDispatcherForExpiredToken() = object :Dispatcher() {<br>  override fun dispatch(request: RecordedRequest): MockResponse{<br>    return when (request.path) {<br>      authEndpointPath -&gt; { MockResponse().setResponseCode(200).setBody(<br>JsonReader.getJson(&quot;networkresponses/validToken.json&quot;)<br>      }<br>      endpointPath -&gt;{ MockResponse().setResponseCode(200) }<br>      else -&gt; { MockResponse().setResponseCode(404) }<br>    }<br>  }<br>}</pre><p>Finalmente podemos recuperar la respuesta de nuestro server de la siguiente forma:</p><pre>val request = mockWebServer.takeRequest()</pre><p>Podríamos hacer algunas verificaciones en el test tales como:</p><pre>with(request) { // 2<br>  assertThat(method).isEqualTo(&quot;GET&quot;)<br>  assertThat(path).isEqualTo(animalsEndpointPath)<br>  assertThat(getHeader(ApiParameters.AUTH_HEADER)).isEqualTo(ApiParameters.TOKEN_TYPE + validToken)<br>}</pre><p>Repasemos lo que hemos visto hasta aquí. Primero vimos que es MockWebServer y para nos ayuda en nuestros tests. Luego aprendimos como añadir un archivo .json con el contenido de la respuesta de nuestro servidor ficticio, además de crear un objeto que nos ayuda a convertir este json en un String reconocible para nuestro código. Lo siguiente fue como añadir la dependencia del servidor a nuestro test y como configurarla<br>Finalmente tocamos como personalizar la respuesta de nuestro server usando el dispatcher.</p><p>El uso de este server en nuestro test depende de la lógica en nuestra app.</p><p>Si has llegado hasta este punto tienes las bases para iniciarte en el testing del network de tu app. Sé que hay mucho más de que hablar pero cómo te dije estas son las bases. Sígueme que estaré publicando más contenido de testing y del mundo del desarrollo de Android. Gracias : )</p><p>Agradeceré cualquier comentario constructivo</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c275db1a7e5c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tests Unitarios en Andorid parte 1]]></title>
            <link>https://medium.com/@janderlaffita/tests-unitarios-en-andorid-parte-1-4b4475de2303?source=rss-9bb141b7ac54------2</link>
            <guid isPermaLink="false">https://medium.com/p/4b4475de2303</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-testing]]></category>
            <dc:creator><![CDATA[Jander Laffita Orduñez]]></dc:creator>
            <pubDate>Wed, 08 Mar 2023 18:33:58 GMT</pubDate>
            <atom:updated>2023-03-08T18:33:58.597Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*DGXzPprAHXzTMnMjiDfCZQ.png" /></figure><p>Hola a todos los que se han tomado tiempo para leer este post, espero poder ayudarlos en sus dudas acerca de testing en Android. Al tratarse del primer post permítanme una pequeña presentación. Mi nombre es <strong>Jander Laffita</strong> graduado de Ciencias Médicas pero apasionado del código y programador de vocación y profesión : ). Actualmente trabajando para<strong> Jade Innovaciones Informáticas</strong>, una empresa de desarrollo de Software en general. Esta será una serie de artículos cortos basados en las<em> guías de Google</em>, que en principio comenzaron siendo mis notas de estudio y quiero compartir con ustedes.</p><p>El testing es una parte integral del proceso de desarrollo. Al ejecutar pruebas puedes verificar la corrección, el comportamiento funcional y la usabilidad de la app antes de lanzarla públicamente.</p><p><strong>Categorías de pruebas:</strong><br> 1- Pruebas de nivel inferior (pruebas de unidades) <strong><em>70%</em></strong><br> 2- Pruebas de nivel medio (pruebas de integración) <strong><em>20%</em></strong><br> 3- Pruebas de nivel superior (pruebas de extremo a extremo) <strong><em>10%</em></strong></p><p><em>El porcentaje representa la distribución recomendada por Google de los test en tus apps.</em></p><p>Vamos a dar un breve repaso a cada tipo de test.</p><p><strong>Pruebas de nivel inferior:</strong> deben ser pruebas unitarias q validen la funcionalidad de cada clase dentro de la app. Si las pruebas dependen del framework de android usa una API unificada independiente del dispositivo (androidx.test).Esta te permite ejecutar la prueba de forma local sin un dispositivo físico ni un emulador.</p><p><strong>Pruebas de nivel intermedio:</strong> Validan la colaboración y la interacción de un grupo de unidades</p><p><em>Ejemplos:</em><br>-&gt; interaccion vista modelo (Fragment-XML, Fragment-ViewModel)<br>-&gt; repositorio (data sources — DAO)<br>-&gt; porciones verticales (todas las capas de una pantalla determinada)<br>-&gt; varios fragmentos en un área específica de la app</p><p><strong>Pruebas Unitarias: </strong>Ejercitan la funcionalidad de la unidad de código más pequeña posible (que puede ser un método, una clase o un componente) de manera repetible. Debes crear pruebas de unidades cuando necesites verificar la lógica de un código específico en tu app. Por ejemplo, si pruebas una unidad en una clase, la prueba podría verificar que la clase esté en el estado correcto.</p><p>Se dividen en: <em>Pruebas locales,</em> pruebas de unidades que se ejecutan solo en la máquina local. <em>Pruebas instrumentadas,</em> pruebas que se ejecutan en un dispositivo o en un emulador.</p><p>En esta primera parte de esta serie vamos a acercarnos a las <strong>pruebas locales</strong>.<br>Si tienes dependencias del framework de android se recomienda usar Robolectric. Si tienes dependencias mínimas del framework de android o solo dependen de sus propios objetos, usa Mockito</p><p>Añade al archivo gradle a nivel de módulo las dependencias q vallas a necesitar según lo expuesto en el párrafo anterior</p><pre> dependencies {<br>        // Required -- JUnit 4 framework<br>        testImplementation &#39;junit:junit:4.12&#39;<br>        // Optional -- Robolectric environment<br>        testImplementation &#39;androidx.test:core:1.0.0&#39;<br>        // Optional -- Mockito framework<br>        testImplementation &#39;org.mockito:mockito-core:1.10.19&#39;<br>    }</pre><p>Para crear una clase de prueba, crea una clase q contenga uno o más métodos de prueba ( Anotación <a href="http://twitter.com/Test">@Test</a> )</p><p>Ejemplo:</p><pre>import com.google.common.truth.Truth.assertThat<br>    import org.junit.Test<br><br>    class EmailValidatorTest {<br>        @Test<br>        fun emailValidator_CorrectEmailSimple_ReturnsTrue() {<br>            assertThat(EmailValidator.isValidEmail(&quot;name@email.com&quot;)).isTrue()<br>        }<br>    }</pre><p><strong>¿ Como añadir dependencias del Framework ?</strong></p><p>Si tus pruebas interactúan con varias dependencias del framework de Android usa las clases q proporciona Robolectric</p><pre>import android.content.Context<br>    import androidx.test.core.app.ApplicationProvider<br>    import com.google.common.truth.Truth.assertThat<br>    import org.junit.Test<br><br>    private const val FAKE_STRING = &quot;HELLO_WORLD&quot;<br><br>    class UnitTestSample {<br>        val context = ApplicationProvider.getApplicationContext&lt;Context&gt;()<br><br>        @Test fun readStringFromContext_LocalizedString() {<br>            // Given a Context object retrieved from Robolectric...<br>            val myObjectUnderTest = ClassUnderTest(context)<br><br>            // ...when the string is returned from the object under test...<br>            val result: String = myObjectUnderTest.getHelloWorldString()<br><br>            // ...then the result should be the expected one.<br>            assertThat(result).isEqualTo(FAKE_STRING)<br>        }<br>    }</pre><p><strong>¿ Como incluir dependencias ficticias o mocks ?</strong></p><p>Las llamadas a clases del framework de android desde un test arrojará una excepción. Esto es para asegurarse q solo estás probando código y no comportamientos de Android.</p><p><em>¿ Como generar dependencias de android “mockeadas” :) ?</em></p><p>Mockito es una librería donde puedes configurar objetos ficticios para mostrar un valor especifico cuando se les llama</p><p><strong>Para agregar un mock sigue estos pasos:</strong><br>1- Incluye la dependencia de Mockito en el gradle <br>2- Al comienzo de la definición de la clase agrega la anotación <a href="http://twitter.com/RunWith">@RunWith</a>(MockitoJUnitRunner.class) este objeto le indica a Mockito<br>que el uso de tu framework es correcto y simplifica la inicialización de los mocks.<br>3- A fin de crear un mock para una dependencia de andorid agrega la anotación <a href="http://twitter.com/MOck">@M</a>o<a href="http://twitter.com/MOck">ck</a> antes de la declaración de campo<br>4 Para indicar el comportamiento de la dependencia puedes especificar una condición y el valor mostrado cuando se cumpla la condición<br>mediante los métodos when() y thenReturn(). <em>Este when no se refiere al bucle sino a un método público de Mockito.</em></p><p>El siguiente ejemplo muestra cómo crear un mock de Context:</p><pre>import android.content.Context<br>    import com.google.common.truth.Truth.assertThat<br>    import org.junit.Test<br>    import org.junit.runner.RunWith<br>    import org.mockito.Mock<br>    import org.mockito.Mockito.`when`<br>    import org.mockito.junit.MockitoJUnitRunner<br><br>    private const val FAKE_STRING = &quot;HELLO WORLD&quot;<br><br>    @RunWith(MockitoJUnitRunner::class)<br>    class UnitTestSample {<br><br>        @Mock<br>        private lateinit var mockContext: Context<br><br>        @Test<br>        fun readStringFromContext_LocalizedString() {<br>            // Given a mocked Context injected into the object under test...<br>            `when`(mockContext.getString(R.string.hello_word))<br>                    .thenReturn(FAKE_STRING)<br>            val myObjectUnderTest = ClassUnderTest(mockContext)<br><br>            // ...when the string is returned from the object under test...<br>            val result: String = myObjectUnderTest.getHelloWorldString()<br><br>            // ...then the result should be the expected one.<br>            assertThat(result, `is`(FAKE_STRING))<br>        }<br>    }</pre><p>Hasta este punto has podido añadir <strong>context</strong> como dependencia en tus test usando Robolectric y Mockito. Para mas detalles mira la documentación de Mokito <a href="https://github.com/mockito/mockito">aquí</a></p><p>La lógica a usar en tus test depende claramente del método q estés testeando en tu app. En otro post veremos más de cómo se implementa esta a más detalle. En la parte 2 de esta serie veremos como usar las <strong>pruebas de unidades instrumentadas</strong>. Gracias por llegar hasta esta parte del post. Sígueme si quieres aprender más del mundo de android y la programación en general.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b4475de2303" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>