Haciendo Pruebas Automatizadas en Laravel [PARTE II] – Unit Test

Laravel: Guia de unit tests

Deja una respuesta

Comment as a guest.

  1. La verdad que la explicación es excelente y con ejemplos muy útiles , me ayudó bastante , justo estoy en un proyecto con Laravel y quiero comenzar a implementar test . Muchas gracias por el excelente post

    1. Muchas gracias a vos Marcelo por comentar. Y cualquier duda, te esperamos por aquí o por el grupo de facebook «Hablemos de Laravel». Saludos.

  2. Gracias Matias, no conseguia entender estos conceptos que haz explicado a la perfeccion… mil gracias!! Esperando por aprender sobre esas cosas que avanzadas que dejaste en el tintero… Por ahora, acabas de hacer de alguien un mejor desarrollador. Empezare a aplicar lo aprendido.

    1. Hola Vioscar, como estas? Muchas gracias por tu comentario. Me alegra mucho que te haya servido y próximamente seguiré publicando más sobre este tema. Saludos!

  3. Excelente post amigo me sirvió de mucho ya que estoy arrancando un proyecto desde 0 y quería integrar los test automáticos, revise este y el post anterior de este tema y te consulto recomiendas aplicar los dos tipos de test que explicas en ambos artículos, o solo este ultimo? y para el caso de que tenga rutas protegidas (Laravel Passport) por Middleware Auth y role respectivamente como se hace en ese caso que me recomiendas, de antemano muchas gracias, saludos.

    1. Hola Jose, muchas gracias por tu comentario!

      Si, cada tipo de test prueba distintas cosas. Así que, si puedes aplicar los dos tipos, mejor.
      Acordate que los test unitarios deben probar lógica que no pega contra base de datos y el otro tipo de test si.

      Saludos y cualquier duda la podes dejar acá o en el grupo de facebook Hablemos de Laravel.

  4. Hola, excelente post. Tengo una pregunta nada mas, si la funcionalidad no exisitiera aun, es decir no hubieramos codificado la funcionalidad del checkout, deberiamos empezar codificando una primera version de la funcionalidad y tratar de hacerle unit tests a esa primera version (con las refactorizaciones necesarias) o empezamos con el unit test e ir agregando la funcionalidad conforme vaya nuestro unit test como marcan los puristas de TDD?

    1. Me alegro que te gusto el post Carlos!
      Si recién estas aprendiendo a hacer tests, no te recomendaría que encares una nueva funcionalidad con TDD. Porque TDD es un cambio de cabeza de como encaras los desarrollos y puede ser medio frustrante si no tenes experiencia haciendo tests.

      Hacer primero la funcionalidad y después el tests no esta mal, pero tampoco es lo ideal. Pero te da experiencia desarrollando tests.

      En resumen: si la tenes clara haciendo tests, hace la funcionalidad con TDD. Si no, desarrolla la funcionalidad (con código bien feo), aplicale tests y después mejora ese código refactorizando.

      1. Muchas gracias por tu respuesta y tu tiempo. Si, apenas ahora ando tratando de profesionalizar mi desarrollo y estoy tratando de aplicar TDD y si es frustrante al principio, sobre todo cuando no estas muy seguro por donde empezar. Pero tanto este post como el otro que hiciste me han ayudado mucho y abierto un panorama un poco mas claro. Lo que estoy tratando de hacer es de no encuadrarme 100% en como se debe de hacer, si no como dices, tomarle el feeling primero. Solo tenia duda si no me estaba engañando a mi mismo y deberia de seguir a raja tabla la metodologia. Por cierto, mucho gusto, soy de Mexico, saludos hasta ahi a Argentina (estoy asumiendo que ahi te encuentras). Ya me excedí en el mensaje jeje. Abusando de tu amabilidad, una pregunta, un sistema de descuentos dinamico (es decir, el usuario de la aplicacion define los descuentos, los periodos, etc) como lo atacarias? Con un strategy pattern o con un decorator pattern? O con ninguno de los dos. Tengo dificultad en mi cabeza como atacar este problema.

        Saludos y gracias.

        1. No es ninguna molestia Carlos, al contrario, me gusta hablar con la gente de la comunidad.
          Por qué pensaste atacarlo con decorator o strategy? Te lo pregunto más que nada para entender la lógica de tu negocio. De movida te diría que el decorator no, pero capaz que estás queriendo resolver una lógica y el decorator encaja bien.

        2. Mas que nada porque segun mi entendimiento el decorator es para darle nuevas responsabilidades a una clase pero en tiempo de ejecucion. Entonces, como mi esquema de descuentos es dinamico, cuando el usuario cree un nuevo tipo de descuento, la aplicacion de alguna forma sin tocar el codigo aplique este descuento a los productos que se configuren con este descuento. Aclaro que aun estoy en la fase de tormenta de ideas de como implementar esta funcionalidad. En pseudocodigo mas o menos tendria pensado algo asi:
          Product es mi modelo con su unit price, Orden y OrdenDetails tiene estos productos y tendria una clase que recibe la orden y calcula su monto total en base a la qty y el up de cada producto, pero antes verificaria si este producto tiene definido un descuento por lo que tendria un decorator que tome el producto y le aplique los descuentos definidos en la base de datos por el usuario de la aplicacion. Como no se de antemano que descuentos estos serian, veo aun mas complicado poder aplicarle un strategy porque como definiria en tiempo de ejecucion que estrategia generar y aplicar. No se si estoy siendo claro.
          Saludos.

        3. Claro, tiene sentido! Y una consulta, ¿el usuario va a poder elegir entre unos descuentos ya pre-existentes (por ejemplo, 2×1, 3×2, 20% de descuento, etc.) o el usuario va a poder customizar todo?

        4. Probablemente tenga pre-cargados los mas comunes, 50% de descuento, 25% de descuento, los que comentas de 2 x 1, no costo de shipping. Pero el cliente dejo en claro que le gustaria poder crear sus propios descuentos asi como tambien manejarlos por periodos, como por ejemplo en nuestro Buen Fin (un fin de semana de 4 dias que viene siendo como una copia barata del Black Friday de los gringos), etc.

        5. Bieen, lindo laburo vas a tener! Vas a tener que aplicar más cosas a demás del decorator, capaz que un abstract factory 🤔.
          Pero bueno, anda de a poco y entregandole valor de apoco al cliente para que no joda jejej (MVP) y cualquier duda ya sabes que podes pasar por acá o por el grupo de facebook «Hablemos de Laravel».

        6. Jajaja, si, así sera, pero igual quiero estar preparado para cualquier cosa. No habia pensado en lo del Abstract Factory, pero ahora que lo mencionas, esta dando vueltas mi cabeza en como ya podria aplicarlo jeje. Claro que si, aqui me tendras mucho tiempo moliendo gente jeje. Ya me eche casi todos los articulos que has subido, al jefe de mi trabajo regular no le va a gustar la procrastinacion jejeje, sobre todo porque aqui no usamos php ni laravel.
          Saludos y que estes bien.

  5. Amigo la ultima prueba me genera ese problema: 1) Tests\Feature\app\Services\PromoCalculatorTest::Se_Verifica_Excepcion_Cuando_No_Hay__Productos
    PHPUnit\Framework\Exception: Class Tests\Feature\app\Services\EmptyProductsException does not exist, como lo puedo solucionar?

    1. Tienes un error en como importantes la excepción. Fijate que el error comienza con el namespace Test: Tests\Feature\app\Services\EmptyProductsException.
      Arregla el use en el test o pone una contrabarra cuando vayas a usar el tests, por ejemplo $this->expectException(\EmptyProductsException);. Yo te recomiendo que arregles el use. Saludos.

  6. Estoy aprendiendo también Laravel y me ha gustado mucho tu post. Gracias!

    Me surge una duda en la estructura. Siempre he leído que en los controladores solo se deben poner las funciones ‘index, create, store, show, edit, update and destroy’
    ¿Como de correcto es meter la función calculatePromo() en un controlador?
    ¿Sería mejor meterla en un Trait?

    Un saludo!

    1. Hola Pablo, como estás? Muchas gracias por tu comentario.
      Con respecto a que los controladores deben tener solo los métodos de un CRUD (index, show, create, update, etc.) no es correcto. Porque, no siempre un controlador pertenece a un sistema tipo CRUD. Hay aplicaciones mucho más complejas que conviene poner un nombre más descriptivo, como calculatePromo por ejemplo.

      Hasta pueden existir sistemas CRUD complejos donde hay un controlador por cada método. Por ejemplo, CreateProductController o UpdateProductController, etc. Esto último se hace para tener controladores más fácil de mantener, testear y depurar.

  7. El comando: `php artisan make:test app/Services/PromoCalculatorTest`
    en Laravel 7, genera la clase en «tests/Features/» (directorio por defecto).
    Entonces el namespace de la clase será: `Tests\Feature\app\Services`.
    Para crear la clase bajo el directorio Unit se debe añadir el flag: –unit
    `php artisan make:test app/Services/PromoCalculatorTest –unit`
    Y ahora sí, el namespace de la clase PromoCalculatorTest es: `Tests\Unit\app\Services`

  8. Muchas gracias por el articulo, la verdad q esta web es invaluable.
    tengo una pregunta vi que escribiste EmptyProductsExeption esta excepcion la creaste vos de manera personalizada? o es de esas cosas que entrega laravel del estilo PalabraMODELOPalabra devolviendo un resultado? no se si me explico.

    1. Hola Javier, como estas? Muchas gracias por tu comentario!
      Si, es una clase que cree yo. El código sería class EmptyProductsException extends Exception y lo haga para que el código se entienda más a la hora de leerlo.

  9. Interesante. recomiendas entonces primero si o si hacer primero los test luego escribir código en el controlador ? ,
    o van de la mano

    1. Lo ideal es aplicar una práctica que se llama TDD (Test Driven Design) que dice que primero escribas un pequeño test, luego escribas código para que pase ese test, después refactorizas el código escrito y volves a repetir. Esta técnica es muy buena si previamente conoces todos los casos de pruebas que tenes que hacer para que funcione correctamente tu aplicación.
      Te recomiendo mucho que leas sobre esta práctica porque te lleva al siguiente nivel como desarrollador.

  10. Excelente post, la verdad siempre me costo aplicar unit tests en laravel pero la sugerencia del refactoreo para procesar la logica en otro lugar no solamente tiene mucho sentido sino que permite utilizar los unit tests en otros lugares.

    Ahora, tengo una pequeña pregunta. Supon que estas trabajando con un sistema heredado y debes implementar tests a una clase que tiene mucha logica encima.
    Entre la logica ademas hay llamadas a base de datos y por una razon que no discutiremos ahora no es factible realizar un refactor para separar bien las responsabilidades.

    Al integrar la base de datos al test dejaria de ser unitario si mal no entiendo. ¿Que puedes sugerir para evitar esto?¿Podria utilizar por ejemplo un mock de una respuesta esperada?

    La clase por lo menos separa las consultas de base de datos a otras funciones, podria reemplazar el resultado de esta llamada con php unit? No es necesario entrar en detalle en este tema, pero me serviria si me puedes compartir algun articulo que me sea util

Sliding Sidebar

Matias Echazarreta

¡Hola!

Mi nombre es Matias Echazarreta.
Soy desarrollador web con más de 12 años de experiencia. Amante de Laravel, de los libros y del rock de los ’90. Te puedes comunicar conmigo  por trabajos de contratación, haciendo click aquí.

Nuestro Patreon

Desde Patreon puedes solicitar asesoria personalizado. ¡Ir a Patreon!

Suscríbete a nuestra lista de correo