none
Por que usar Moks en proyectos Unit Test xUnit RRS feed

  • Pregunta

  • Hola

    Estoy analizando la implementación de pruebas unitarias a un proyecto web api (soy nuevo en esto). Estoy basandome en unos ejemplos de internet y la verdad he podido realizar las pruebas como lo indican en el siguiente enlace:
    https://code-maze.com/unit-testing-aspnetcore-web-api/

    public class ShoppingItem
        {
            public Guid Id { get; set; }
            [Required]
            public string Name { get; set; }
            public decimal Price { get; set; }
            public string Manufacturer { get; set; }
        }
    public interface IShoppingCartService
        {
            IEnumerable<ShoppingItem> GetAllItems();
            ShoppingItem Add(ShoppingItem newItem);
            ShoppingItem GetById(Guid id);
            void Remove(Guid id);
        }
    public class ShoppingCartService : IShoppingCartService
        {
            public ShoppingItem Add(ShoppingItem newItem)
            {
                throw new NotImplementedException();
            }
            public IEnumerable<ShoppingItem> GetAllItems()
            {
                ShoppingItem d = new ShoppingItem();
                d.Id = Guid.NewGuid();
                d.Name = "Test";
    
                return new List<ShoppingItem>() { d };
            }
            public ShoppingItem GetById(Guid id)
            {
                throw new NotImplementedException();
            }
            public void Remove(Guid id)
            {
                throw new NotImplementedException();
            }
        }

    Lo que hacen es crear una interfaz y una clase que la implementa y en el API inyectan la clase mediante una interfaz. 

    [Route("api/[controller]")]
        [ApiController]
        public class ShoppingCartController : ControllerBase
        {
            private readonly IShoppingCartService _service;
    
            public ShoppingCartController(IShoppingCartService service)
            {
                _service = service;
            }
    
            // GET api/shoppingcart
            [HttpGet]
            public ActionResult<IEnumerable<ShoppingItem>> Get()
            {
                var items = _service.GetAllItems();
                return Ok(items);
            }
        }


    Posteriormente crean un proyecto xUnit y crean algunos tests. 

    public class ShoppingCartServiceFake : IShoppingCartService
        {
            private readonly List<ShoppingItem> _shoppingCart;
    
            public ShoppingCartServiceFake()
            {
                _shoppingCart = new List<ShoppingItem>()
                {
                    new ShoppingItem() { Id = new Guid("ab2bd817-98cd-4cf3-a80a-53ea0cd9c200"),
                        Name = "Orange Juice", Manufacturer="Orange Tree", Price = 5.00M },
                    new ShoppingItem() { Id = new Guid("815accac-fd5b-478a-a9d6-f171a2f6ae7f"),
                        Name = "Diary Milk", Manufacturer="Cow", Price = 4.00M },
                    new ShoppingItem() { Id = new Guid("33704c4a-5b87-464c-bfb6-51971b4d18ad"),
                        Name = "Frozen Pizza", Manufacturer="Uncle Mickey", Price = 12.00M }
                };
            }
    
            public IEnumerable<ShoppingItem> GetAllItems()
            {
                return _shoppingCart;
            }
        }

    Mi duda viene aqui, porque crean una clase Fake? Para mi entender no tiene lógica ya que no están probando el código de la aplicación como tal si no una clase externa. Que hago con probar esa si no es la que usa mi aplicación?

    Saludos

    miércoles, 26 de diciembre de 2018 21:00

Respuestas

  • No hay que confundir las pruebas unitarias con las pruebas de integración.

    En las pruebas de integración se prueba efectivamente el código tal como tú dices que quieres probarlo, es decir, llamando a todas las clases y todas sus dependencias incluidas las externas. Esto implica que en general es una prueba lenta y costosa, puesto que hay que configurar todas esas dependencias externas de forma que se conozca exactamente qué es lo que van a devolver en las condiciones de la prueba (por ejemplo, si dependes de una base de datos, la base de datos hay que precargarla antes de la prueba con un conjunto de registros exactamente conocidos antes de poder lanzar la prueba de integración). Es correcto que existan las pruebas de integración, y se deben hacer, pero debido a su lentitud y coste se hacen con poca frecuencia, típicamente antes de liberar cada "release" del producto dentro del ciclo de desarrollo. En estas pruebas en general no se usan Fakes.

    En cambio las pruebas unitarias están dirigidas a probar la mínima unidad ejecutable del código, típicamente un único método. Para poder comprobar si el método por sí solo funciona correctamente, hay que eliminar todas las dependencias externas. Para ello, se inyecta un "fake" en lugar de la dependencia, de forma que conozcamos exactamente qué datos son los que devuelve ese fake y por lo tanto podamos evaluar si el método bajo prueba está realizando los cálculos correctos a partir de esos datos. Estas pruebas, a diferencia de las de integración, son muy rápidas en su ejecuicón y en consecuencia se hacen con mucha frecuencia. Típicamente se hacen al menos una vez antes de hacer el check-in del código al control de código fuente, y a veces mucho más a menudo; incluso la última versión de Visual Studio tiene la opción de ejecutarlas "en vivo" sobre la marcha según vas editando el código fuente.


    miércoles, 26 de diciembre de 2018 21:13
    Moderador
  • hola

    >>Para mi entender no tiene lógica ya que no están probando el código de la aplicación como tal si no una clase externa

    el tema pasa por cual funcionalidad quieres probar

    si tienes una clase de servicio y una de repository (la cual accede a la db), esta claro que vas a mockear la implementacion del repository porque los unit test no acceden a la db fisica, pero si podas probar la logica del servicio

    cuando quieras probar el codigo de repository ya no sera unit test sino que pasaran a ser integration test, para lo cual requieres armar infraestructura y tener en el ambiente de test una db real que pueda ser reconstruida en cada ejecucion de los test

    en tu codigo no tienes cada de servicio, sino que este es tu repository por lo cual lo que estas probando es la logica que pones directo en el controller, probar recuperar datos como unit test tiene poco sentido sino hay logica que validar, o sea en ese codigo esta de mas el test, deberia tener algunas condiciones, filtros, calculos, algo interesante que validar

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina


    • Editado Leandro TuttiniMVP miércoles, 26 de diciembre de 2018 21:07
    • Marcado como respuesta AdyIr jueves, 27 de diciembre de 2018 16:15
    miércoles, 26 de diciembre de 2018 21:06

Todas las respuestas

  • hola

    >>Para mi entender no tiene lógica ya que no están probando el código de la aplicación como tal si no una clase externa

    el tema pasa por cual funcionalidad quieres probar

    si tienes una clase de servicio y una de repository (la cual accede a la db), esta claro que vas a mockear la implementacion del repository porque los unit test no acceden a la db fisica, pero si podas probar la logica del servicio

    cuando quieras probar el codigo de repository ya no sera unit test sino que pasaran a ser integration test, para lo cual requieres armar infraestructura y tener en el ambiente de test una db real que pueda ser reconstruida en cada ejecucion de los test

    en tu codigo no tienes cada de servicio, sino que este es tu repository por lo cual lo que estas probando es la logica que pones directo en el controller, probar recuperar datos como unit test tiene poco sentido sino hay logica que validar, o sea en ese codigo esta de mas el test, deberia tener algunas condiciones, filtros, calculos, algo interesante que validar

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina


    • Editado Leandro TuttiniMVP miércoles, 26 de diciembre de 2018 21:07
    • Marcado como respuesta AdyIr jueves, 27 de diciembre de 2018 16:15
    miércoles, 26 de diciembre de 2018 21:06
  • No hay que confundir las pruebas unitarias con las pruebas de integración.

    En las pruebas de integración se prueba efectivamente el código tal como tú dices que quieres probarlo, es decir, llamando a todas las clases y todas sus dependencias incluidas las externas. Esto implica que en general es una prueba lenta y costosa, puesto que hay que configurar todas esas dependencias externas de forma que se conozca exactamente qué es lo que van a devolver en las condiciones de la prueba (por ejemplo, si dependes de una base de datos, la base de datos hay que precargarla antes de la prueba con un conjunto de registros exactamente conocidos antes de poder lanzar la prueba de integración). Es correcto que existan las pruebas de integración, y se deben hacer, pero debido a su lentitud y coste se hacen con poca frecuencia, típicamente antes de liberar cada "release" del producto dentro del ciclo de desarrollo. En estas pruebas en general no se usan Fakes.

    En cambio las pruebas unitarias están dirigidas a probar la mínima unidad ejecutable del código, típicamente un único método. Para poder comprobar si el método por sí solo funciona correctamente, hay que eliminar todas las dependencias externas. Para ello, se inyecta un "fake" en lugar de la dependencia, de forma que conozcamos exactamente qué datos son los que devuelve ese fake y por lo tanto podamos evaluar si el método bajo prueba está realizando los cálculos correctos a partir de esos datos. Estas pruebas, a diferencia de las de integración, son muy rápidas en su ejecuicón y en consecuencia se hacen con mucha frecuencia. Típicamente se hacen al menos una vez antes de hacer el check-in del código al control de código fuente, y a veces mucho más a menudo; incluso la última versión de Visual Studio tiene la opción de ejecutarlas "en vivo" sobre la marcha según vas editando el código fuente.


    miércoles, 26 de diciembre de 2018 21:13
    Moderador
  • hola

    >>Para mi entender no tiene lógica ya que no están probando el código de la aplicación como tal si no una clase externa

    el tema pasa por cual funcionalidad quieres probar

    si tienes una clase de servicio y una de repository (la cual accede a la db), esta claro que vas a mockear la implementacion del repository porque los unit test no acceden a la db fisica, pero si podas probar la logica del servicio

    cuando quieras probar el codigo de repository ya no sera unit test sino que pasaran a ser integration test, para lo cual requieres armar infraestructura y tener en el ambiente de test una db real que pueda ser reconstruida en cada ejecucion de los test

    en tu codigo no tienes cada de servicio, sino que este es tu repository por lo cual lo que estas probando es la logica que pones directo en el controller, probar recuperar datos como unit test tiene poco sentido sino hay logica que validar, o sea en ese codigo esta de mas el test, deberia tener algunas condiciones, filtros, calculos, algo interesante que validar

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina


    Muchas gracias Leandro, estaba entonces errado en mis conceptos...
    jueves, 27 de diciembre de 2018 16:15
  • No hay que confundir las pruebas unitarias con las pruebas de integración.

    En las pruebas de integración se prueba efectivamente el código tal como tú dices que quieres probarlo, es decir, llamando a todas las clases y todas sus dependencias incluidas las externas. Esto implica que en general es una prueba lenta y costosa, puesto que hay que configurar todas esas dependencias externas de forma que se conozca exactamente qué es lo que van a devolver en las condiciones de la prueba (por ejemplo, si dependes de una base de datos, la base de datos hay que precargarla antes de la prueba con un conjunto de registros exactamente conocidos antes de poder lanzar la prueba de integración). Es correcto que existan las pruebas de integración, y se deben hacer, pero debido a su lentitud y coste se hacen con poca frecuencia, típicamente antes de liberar cada "release" del producto dentro del ciclo de desarrollo. En estas pruebas en general no se usan Fakes.

    En cambio las pruebas unitarias están dirigidas a probar la mínima unidad ejecutable del código, típicamente un único método. Para poder comprobar si el método por sí solo funciona correctamente, hay que eliminar todas las dependencias externas. Para ello, se inyecta un "fake" en lugar de la dependencia, de forma que conozcamos exactamente qué datos son los que devuelve ese fake y por lo tanto podamos evaluar si el método bajo prueba está realizando los cálculos correctos a partir de esos datos. Estas pruebas, a diferencia de las de integración, son muy rápidas en su ejecuicón y en consecuencia se hacen con mucha frecuencia. Típicamente se hacen al menos una vez antes de hacer el check-in del código al control de código fuente, y a veces mucho más a menudo; incluso la última versión de Visual Studio tiene la opción de ejecutarlas "en vivo" sobre la marcha según vas editando el código fuente.


    Hola Alberto, muchas gracias. Tenia una visión distinta.
    jueves, 27 de diciembre de 2018 16:16