Anuncios

Bienvenidos sean a este post, hoy veremos un atributo bastante particular.

Anuncios

Hasta ahora hemos visto una macro que nos permite evaluar valores de distintas formas y en caso de no ser verdadera (true) procede a llamar a panic para fallar en el test pero tenemos una opcion mas para poder verificar posibles errores en nuestros test y ahi entra en accion este astributo.

Anuncios

Para entenderlo vamos a crear un nuevo proyecto que usaremos para asignar el id de los usuarios invitados en una empresa X, para ello lo llamaremos invitados, como sera de tipo libreria usaremos este comando:

$ cargo new invitados --lib
Anuncios

Una vez creado vamos a modificar el codigo de lib.rs de la siguiente manera:

lib.rs

pub struct Invitado {
	valor: u32,
}

impl Invitado
{
	pub fn nuevo(valor: u32) -> Invitado {
		if valor < 20 || valor > 70 
		{
			panic!("El id debe estar entre 20 y 70, pasaste {}",
				valor);
		}
		Invitado { valor }
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn crear_invitado()
	{
		Invitado::nuevo(10);
	}
}
Anuncios
Anuncios

Primero crearemos un struct que llamaremos Invitado con una sola propiedad llamada valor, luego definiremos una implementacion, en este caso el metodo encargado de crear uno nuevo, en ella recibira un valor, y ahi utilizaremos un condicional donde verifica si el valor es menor a 20 o mayor a 70 y en caso de cumplirse llamara a panic para que nos muestre el mensaje indicando que el id debe estar entre estos valores, y pasamos el valor, en caso de no cumplirse el condicional creara el objeto y lo devolvera, en el main usamos a super para indicarle que usaremos el modulo base, y despues simplemente un atributo de test con una funcion que llamara a nuevo para crear un nuevo invitado, compilemos y veamos su salida:

    Finished test [unoptimized + debuginfo] target(s) in 1.30s
     Running unittests (target/debug/deps/invitados-06ae58bb5146574f)

running 1 test
test tests::crear_invitado ... FAILED

failures:

---- tests::crear_invitado stdout ----
thread 'main' panicked at 'El id debe estar entre 20 y 70, pasaste 10', src/lib.rs:10:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::crear_invitado

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
tinchicus@dbn001vrt:~/lenguajes/rust/invitados$
Anuncios

Nos notificara que no usamos a la propiedad del struct por eso no se los muestro, vamos a lo que nos interesa, en este caso observamos que fallo y esto es debido a que pasamos un valor que no esta entre 20 y 70, vamos a modificar el mod tests de la siguiente forma:

mod tests {
        use super::*;

        #[test]
        #[should_panic]
        fn crear_invitado()
        {
                Invitado::nuevo(10);
        }
}
Anuncios

En este caso agregamos el atributo should_panic para indicarle que vamos a esperar que el codigo entre en panico, observen que es la unica diferencia con el anterior, el resto es exactamente el mismo, corramos el test nuevamente y veamos la salida:

    Finished test [unoptimized + debuginfo] target(s) in 1.20s
     Running unittests (target/debug/deps/invitados-06ae58bb5146574f)

running 1 test
test tests::crear_invitado - should panic ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests invitados

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

tinchicus@dbn001vrt:~/lenguajes/rust/invitados$
Anuncios

Como pueden ver ahora si funciona dado que esperaba un panic y este ocurrio por lo tanto el test funciono correctamente pero no nos indico cual fue el motivo del panico, para ello debemos hacer un par de modificaciones, la primera sera en nuestra implementacion y la modificaremos de la siguiente manera:

impl Invitado
{
	pub fn nuevo(valor: u32) -> Invitado {
		if valor < 20
		{
			panic!("El id debe ser mayor a 20, pasaste {}", valor);
		} else if valor > 70 {
			panic!("El id debe ser menor a 70, pasaste {}", valor);
		}		

		Invitado { valor }
	}
}
Anuncios

En este caso la modificacion que hicimos fue eliminar la condicion de evaluacion entre dos valores para convertirla en dos condiciones individuales, siendo cada una con su respectivo mensaje, en este caso una indicando que debe ser mayor a 20 y otra indicando que sea menor a 70, el resto sigue igual, la siguiente modificacion sera en el mod:

mod tests {
	use super::*;

	#[test]
	#[should_panic(expected = "El id debe ser mayor a 20")]
	fn crear_invitado()
	{
		Invitado::nuevo(10);
	}
}
Anuncios

En este caso agregamos expected y le decimos al should_panic que debe estar a la espera de este mensaje, que es una de las posibilidades que nos puede devovler el panic, con esto consideramos que el test pasara ok si el panic devuelve este mensaje, antes de compilarlo veamos como quedo el codigo hasta ahora:

lib.rs

pub struct Invitado {
	valor: u32,
}

impl Invitado
{
	pub fn nuevo(valor: u32) -> Invitado {
		if valor < 20
		{
			panic!("El id debe ser mayor a 20, pasaste {}", valor);
		} else if valor > 70 {
			panic!("El id debe ser menor a 70, pasaste {}", valor);
		}		

		Invitado { valor }
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	#[should_panic(expected = "El id debe ser mayor a 20")]
	fn crear_invitado()
	{
		Invitado::nuevo(10);
	}
}
Anuncios

Con todo esto comentado si vuelven a correr el test funcionara perfectmente porque es justamente lo que espera el should_panic pero que suceder si tomamos esta linea:

Invitado::nuevo(10);
Anuncios

Y la modificamos de la siguiente manera:

Invitado::nuevo(90);
Anuncios

Si volvemos a correr el test obtendremos lo siguiente:

    Finished test [unoptimized + debuginfo] target(s) in 1.30s
     Running unittests (target/debug/deps/invitados-06ae58bb5146574f)

running 1 test
test tests::crear_invitado - should panic ... FAILED

failures:

---- tests::crear_invitado stdout ----
thread 'main' panicked at 'El id debe ser menor a 70, pasaste 90', src/lib.rs:12:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: panic did not contain expected string
      panic message: `"El id debe ser menor a 70, pasaste 90"`,
 expected substring: `"El id debe ser mayor a 20"`

failures:
    tests::crear_invitado

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'
tinchicus@dbn001vrt:~/lenguajes/rust/invitados$
Anuncios
Anuncios

En este caso podemos ver que fallo y entre las notificaciones que nos devolvio es para informarnos que el esperaba un mensaje de panic y se encontro con otro, por lo tanto no se cumplio el test, asi tendremos una mejor forma de poder saber porque motivo puede fallar un should_panic, entonces con esto tenemos otra forma de verificar nuestros test en complemento con assert, y podemos evaluar eventuales panicos.

Anuncios

En resumen, hoy hemos visto should_panic, un atributo que nos permite evaluar posibilidades de panico en nuestro codigo, vimos un ejemplo para ponerlo en practica, donde vimos todas las posibilidades que puedan ocurrir, por ultimo tenemos una forma de poder filtrar el tipo de panic que puede ocurrir, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios
pp258

Donación

Es para mantenimento del sitio, gracias!

$1.50