Decidí comenzar a escribir pruebas unitarias en nuestra aplicación. Utiliza Entity Framework con un patrón de repositorio.Prueba de unidad EF Repositorio patrón con Moq
Ahora quiero comenzar a probar las clases de lógica que están utilizando los repositorios. Proporciono un ejemplo simple aquí.
Tres de mis métodos en el GenericRepository clase:
public class GenericRepository : IRepository
{
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return Context.CreateQuery<TEntity>(entityName);
}
private string GetEntityName<TEntity>() where TEntity : class
{
return typeof(TEntity).Name;
}
public IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
return GetQuery<TEntity>().Where(predicate).AsEnumerable();
}
}
Una clase simple lógica regresando años distintos de una tabla de calendario en orden descendente (sí sé la palabra calendario está mal escrito en nuestro código):
public class GetDistinctYearsFromCalendar
{
private readonly IRepository _repository;
public GetDistinctYearsFromCalendar()
{
_repository = new GenericRepository();
}
internal GetDistinctYearsFromCalendar(IRepository repository)
{
_repository = repository;
}
public int[] Get()
{
return _repository.Find<Calender_Tbl>(c => c.Year.HasValue).Select(c => c.Year.Value).Distinct().OrderBy(c => c).Reverse().ToArray();
}
}
Y aquí es mi primera prueba:
[TestFixture]
public class GetDistinctYearsFromCalendarTest
{
[Test]
public void ReturnsDistinctDatesInCorrectOrder()
{
var repositoryMock = new Mock<IRepository>();
repositoryMock.Setup(r => r.Find<Calender_Tbl>(c => c.Year.HasValue)).Returns(new List<Calender_Tbl>
{
new Calender_Tbl
{
Date =
new DateTime(2010, 1, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2010, 2, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2011, 1, 1),
Year = 2011
}
}.AsQueryable());
var getDistinct = new GetDistinctYearsFromCalendar(repositoryMock.Object).Get();
Assert.AreEqual(2, getDistinct.Count(), "Returns more years than distinct.");
Assert.AreEqual(2011, getDistinct[0], "Incorrect order, latest years not first.");
Assert.AreEqual(2010, getDistinct[1], "Wrong year.");
}
}
Este está funcionando bien Pero esto no es realmente lo que quiero hacer. Como tengo que configurar el método Buscar en el objeto simulado, también necesito saber cómo se llamará en mi clase lógica. Si me gustaría hacer TDD, no quiero que me importe esto. Todo lo que quiero saber es qué entidades de calendario debería proporcionar mi repositorio. Me gustaría configurar el método GetQuery. De esta manera:
repositoryMock.Setup(r => r.GetQuery<Calender_Tbl>()).Returns(new List<Calender_Tbl>
{
new Calender_Tbl
{
Date =
new DateTime(2010, 1, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2010, 2, 1),
Year = 2010
},
new Calender_Tbl
{
Date =
new DateTime(2011, 1, 1),
Year = 2011
}
}.AsQueryable());
Así que cuando Find se llama GetQuery internamente en la clase GenericRepository que deben recibir las entidades calendario correcto que tengo que instalar en GetQuery. Pero esto no está funcionando, por supuesto. Como no configuré el método Buscar de mi objeto falso, no obtengo ninguna entidad.
¿Qué hacer? Por supuesto, probablemente podría usar Moles o algún otro framework que se burle de todo, pero no quiero hacer eso. ¿Hay algo que pueda hacer en el diseño de la clase o prueba para resolver el problema?
No es el fin del mundo si tengo que ir con mi solución actual, pero ¿qué pasa si el año de la propiedad se convierte en una int no nulable? Entonces, por supuesto, tendré que cambiar mi implementación en la clase lógica, pero también tendría que cambiar la prueba. Me gustaría tratar de evitar esto.
Creo que una forma de hacerlo es hacer que 'GetDistinctYearsFromCalendar' dependa de 'IRepository'. De esta forma, puede burlarse del 'GenericRepository' una prueba 'GetDistinctYearsFromCalendar' aislada. Consulte http://en.wikipedia.org/wiki/Dependency_injection para obtener más detalles. – Michael
Está bien, no vi el ctor interno. ¿Tienes pruebas y código de producción en el mismo proyecto? Creo que la forma correcta de hacerlo es haciendo la inyección de dependencia por completo y omitir el ctor predeterminado. – Michael
Me doy cuenta de que podría simplemente escribir mi lógica como: return _repository.GetQuery(). Where (c => c.Year.HasValue) .Seleccione (c => c.Year.Value) .Distinct(). OrderBy (c => c) .Reverse(). ToArray(); No Sigo las pruebas en otro proyecto pero expongo las partes internas del proyecto de prueba con las directivas de ensamblaje. ¿Pero qué pasa si nunca uso otro tipo de GenericRepository? ¿Es necesario hacer una inyección de dependencia en todo momento? Esa parece ser la mejor solución ¿verdad? Estaba ciego y quería usar toda la funcionalidad del repositorio. –
John