Fundamentos de Mocks (Pruebas Unitarias)

En esta entrada partimos de lo hablado en la entrada anterior respecto a las pruebas unitarias.

Un mock es un objeto el cual intenta suplantar una dependencia de una clase. La idea es que un objeto mock nos permite crear un objeto del cual tenemos total control, de este modo, podemos definir cuál será el valor de retorno de sus funciones. Desde el punto de vista de las pruebas unitarias, los mocks son importantes porque nos ayudan a enfocar nuestras pruebas en una clase específica sin tener la molestia de lidiar con dependencias. Esto es especialmente importante cuando trabajamos con dependencias externas de nuestro software, como una base de datos, archivos de texto del computador, e incluso web services.

Nuestra misión entonces es utilizar un objeto mock para construir la dependencia que tiene la clase ServicioDeTransferencias, de este modo, podemos concentrarnos en probar dicha clase, y no sus dependencias.

Nos auxiliaremos de la librería Moq para hacer esto. Instala el paquete de Nuget Moq (yo voy a utilizar la versión 4.9).

Básicamente lo que haremos es utilizar Moq para construir un objeto que implemente IServicioValidacionesDeTransferencias y que su función RealizarValidaciones retorne el resultado que nos convenga según la prueba que hagamos. A continuación, nuestras pruebas automáticas que ahora utilizan Moq:

[TestMethod]
public void TransferenciaInvalidaArrojaException()
{
  // Preparación
  Exception expectedException = null;
  Cuenta origen = new Cuenta() { Fondos = 0 };
  Cuenta destino = new Cuenta() { Fondos = 0 };
  decimal montoATransferir = 5m;
  var mock = new Mock<IServicioValidacionesDeTransferencias>();
  string mensajeDeError = "mensaje de error";
  mock.Setup(x => x.RealizarValidaciones(origen, destino, montoATransferir)).Returns(mensajeDeError);
  var servicio = new ServicioDeTransferencias(mock.Object);

  // Prueba
  try
  {
    servicio.TransferirEntreCuentas(origen, destino, montoATransferir);
    Assert.Fail("Un error debió ser arrojado");
  }

  catch(Exception ex)
    {
      expectedException = ex;
    }

  // Verificación
  Assert.IsTrue(expectedException is ApplicationException);
  Assert.AreEqual(mensajeDeError, expectedException.Message);

}

[TestMethod]
public void TransferenciaValidaEditaLosFondosDeLasCuentas()
{
  // Preparación
  Cuenta origen = new Cuenta() { Fondos = 10 };
  Cuenta destino = new Cuenta() { Fondos = 5 };
  decimal montoATransferir = 7m;
  var mock = new Mock<IServicioValidacionesDeTransferencias>();
  mock.Setup(x => x.RealizarValidaciones(origen, destino, montoATransferir)).Returns(string.Empty);
  var servicio = new ServicioDeTransferencias(mock.Object);

  // Prueba
  servicio.TransferirEntreCuentas(origen, destino, montoATransferir);

  // Verificación
  Assert.AreEqual(3, origen.Fondos);
  Assert.AreEqual(12, destino.Fondos);
}

}

Como podemos ver, en ambos métodos declaramos un objeto de nombre mock, el cual va a suplantar la dependencia de la clase que queremos probar. Luego utilizamos el método Setup para indicar que, cuando la función reciba los parámetros indicados, debe retornar lo que colocamos en la función Returns.

De esta manera hemos aislado completamente la funcionalidad de nuestra clase, y así podemos concentrarnos en probarla, sin tener que darles importancia a sus dependencias. Esto es crucial porque, si algunas de nuestras pruebas automáticas fallan, podemos estar seguros de que es debido a que el problema está en la clase que estamos probando, y no en que una dependencia ha dejado de funcionar.

Quiero recalcar, nuevamente, que para hacer una prueba unitaria no es obligatorio hacer mocks de todas las dependencias de nuestras clases. Recuerda que una prueba unitaria es una prueba que verifica una unidad de trabajo, no necesariamente un método de una clase. Como regla general, uno trata de hacer mock de dependencias de bases de datos, web services y otros recursos externos. Sin embargo, para el caso de clases, uno a veces no lo hace, porque se entiende que la relación entre clases es parte de la unidad de trabajo que queremos probar. Al final es un tema de contexto, y tú debes hacer lo que sea más productivo a tu juicio.

Rara vez un software se compone de un método o una clase, y rara vez es que nuestro software no trabaje con componentes externos. Dado esto, es importante también hacer pruebas que verifiquen la armonía entre nuestro software y los componentes externos. Vamos a ver las pruebas de integración en la próxima entrada.

Conclusión

Los mocks nos ayudan a poder controlar el comportamiento de las dependencias de nuestras clases. Esto es útil cuando hacemos pruebas unitarias, pues nos permite fijar el comportamiento de las dependencias de la clase que queremos probar, para así poder concentrarnos en la clase que queremos probar.

Otras entradas de la misma serie:

  1. Conceptos Básicos: Pruebas Automáticas
  2. Pruebas Unitarias
  3. Fundamentos de Mocks (Pruebas Unitarias) (actual)

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s