Return to Snippet

Revision: 35080
at November 2, 2010 07:46 by delm


Initial Code
public class Comuna  {
	public int ComnunaId{get;set;}
	public string Nombre {get;set;}
}

public class Direccion {
	public int DireccionId{get;set;}
	public string Calle {get;set;}
	public string Numeracion {get;set;}
	public Comuna Comuna{ get;set;}
}

public class Persona : IPersona {
	public int PersonaId {get;set;}
	public string Nombre { get; set;}
	public string Apellido {get;set;}
	public Direccion Direccion {get;set;}
}

public interface IViewPersona: IPersona {
	IComando ComandoAceptar {get;set;}
	IComando ComandoCancelar {get;set;}
	IList<Comuna> Comunas {get;set;}
}
public class PresentadorPersona {

	private IPersona memento;
	private IViewPersona view;
	private IRepositorio repo;
	
	public PresentadorPersona(IPersona persona, IViewPersona view, IRepositorio repo) {
		this.view = view;
		this.repo = repo;

		TraspasaValores(persona,this.view); 

		memento = new Persona();
		TraspasaValores(persona,memento); 

		EnlazaComandos();
	}
	
	//Este es un codigo ilustrativo, en la aplicación lo realiza una librería externa
	private void TraspasaValores(IPersona origen, IPersona destino) {
		destino.PersonaId = origen.PersonaID;
		destino.Nombre = origen.Nombre;
		destino.Apellido = origen.Apellido;
		destino.Direccion.DireccionId = origen.Direccion.DireccionID;
		destino.Direccion.Calle = origen.Direccion.Calle;
		destino.Direccion.Numeracion  = origen.Direccion.Numeracion;
		destino.Direccion.Comuna.ComnunaId  = origen.Direccion.Comuna.ComnunaId;
		destino.Direccion.Comuna.Nombre  = origen.Direccion.Comuna.Nombre;
	}
	
	
	public void EnlazaComanodos() {
		//Executed es un evento en la interfaz IComando
		// += es un operador de registro a evento.
		view.ComandoAceptar.Executed += ComandoAceptar;
		view.ComandoCancelar.Executed += ComandoCancelar;
	}
	
	public void ComandoAceptar(){
	
		//tomo es estado de la vista
		IPersona modelo = new Persona();
		TraspasaValores(view,modelo);

		if(ModeloEsValido(modelo){
			repo.Guarda(mdoelo);
			// establezco el estado anterior como válido
			TraspasaValores(view,memento);
		}
	}
	
	public void ComandoCancelar() {
	
		// establezco el valor de la vista en el memento
		TraspasaValores(memento,view);
	}	
}

public class TestPresentadorPersona() {


	public void ComandoCancelar_AlEjecutarce_DebeReestablecerValoresAVista() {
		IPersona persona = CreaPersonaInicial();
		IPersona estadoInicial = CreaPersonaInicial();
		IViewPersona viewMock = MockRepository.CreateMock<IViewPersona>();
		IRepository repoMock = MockRepository.CreateMock<IRepository>();
		PresentadorPersona presentador = new PresentadorPersona(persona, viewMock,repoMock);
		
		ModificaEstadoPersona(persona);
		
		presentador.ComandoCancelar();
		
		Assert.That(persona.PersonaId,Is.EqualTo(estadoInicial.PersonaId));
		Assert.That(persona.Nombre,Is.EqualTo(estadoInicial.Nombre));
		Assert.That(persona.Apellido,Is.EqualTo(estadoInicial.Apellido));
		Assert.That(persona.Direccion.DireccionId,Is.EqualTo(estadoInicial.Direccion.DireccionId));
		Assert.That(persona.Direccion.Calle,Is.EqualTo(estadoInicial.Direccion.Calle));
		Assert.That(persona.Direccion.Numeracion,Is.EqualTo(estadoInicial.Direccion.Numeracion));
		Assert.That(persona.Comuna.ComunaId,Is.EqualTo(estadoInicial.Comuna.ComunaId));
		Assert.That(persona.Comuna.Nombre,Is.EqualTo(estadoInicial.Comuna.Nombre));
	}
	
	public IPersona CreaPersonaInicial(){
		IPersona persona = new Persona();
		persona.PersonaId = 1;
		persona.Nombre = "test nombre";
		persona.Apellido = "test apellido";
		persona.Direccion = new Direccion();
		persona.Direccion.DireccionId = 1;
		persona.Direccion.Calle = "test calle";
		persona.Direccion.Numeracion = "34A test";
		persona.Comuna = new Comuna();
		persona.Comuna.ComunaId = 1;
		persona.Comuna.Nombre = "test comuna";
		return persona;
	}

	public void ModificaEstadoPersona(IPersona persona){
		persona.PersonaId = 2;
		persona.Nombre = "test nombre modificado";
		persona.Apellido = "test apellido modificado";
		persona.Direccion = new Direccion();
		persona.Direccion.DireccionId = 2;
		persona.Direccion.Calle = "test calle modificado";
		persona.Direccion.Numeracion = "34A test modificado";
		persona.Comuna = new Comuna();
		persona.Comuna.ComunaId = 2;
		persona.Comuna.Nombre = "test comuna modificado";
	}

	//Este es un codigo ilustrativo, en la aplicación lo realiza una librería externa
	private void TraspasaValores(IPersona origen, IPersona destino) {
		destino.PersonaId = origen.PersonaID;
		destino.Nombre = origen.Nombre;
		destino.Apellido = origen.Apellido;
		destino.Direccion.DireccionId = origen.Direccion.DireccionID;
		destino.Direccion.Calle = origen.Direccion.Calle;
		destino.Direccion.Numeracion  = origen.Direccion.Numeracion;
		destino.Direccion.Comuna.ComnunaId  = origen.Direccion.Comuna.ComnunaId;
		destino.Direccion.Comuna.Nombre  = origen.Direccion.Comuna.Nombre;
	}
}

Initial URL
http://failfast.chileagil.cl/questions/como-probar-una-implementacion-de-memento

Initial Description
Tengo una implementación legada de un patrón memento para una clase bastante grande, unas 15 propiedades de las cuales 5 de ellas corresponden a clases con sus propias propiedades adicionales.

Este memento está implementado de manera que un presentador tiene en memoria un objeto del mismo tipo que el modelo con el único propósito de mantener el estado previo, el que luego es restaurado si se necesita hacer un rollback de la edición. El traspaso del estado de un objeto a otro se hace propiedad a propiedad (mediante reflection)

Para hacer una refactorización, necesito generar una cobertura de pruebas unitarias decente para asegurar que el funcionamiento de la aplicación no se vea alterado por las mejoras que se introducirán.

Lo complejo, es que me encuentro haciendo pruebas unitarias muy redundantes, probando propiedad por propiedad que sea restablecido su valor al cancelar la edición.

La otra alternativa que tengo, es hacer un conjunto de asserts en un solo test para probar todas las propiedades de una sola vez, pero es tedioso y una conocida mala práctica de pruebas unitarias.

Ninguno de estos approach me deja convencido, ya que al agregar una propiedad en la clase bajo pruebas (que es posible que suceda) los test la van a dejar fuera de cobertura sin alertarme.

¿Se les ocurre alguna forma de mejorar la situación?

Initial Title
Como provar una implementación de Memento.

Initial Tags


Initial Language
C#