I have a setup using Ninject and Moq in my solution. I use Entity Framework, and use the FakeDbSet implementation (see below). This allows me to get GetById, Create, Update and other methods to work, because of the way I've implemented them.
All my services have a method such as:
List<Invoice> GetBySpecification(InvoiceSpecification specification);
This is the only one I cannot easily mock, because my implementation goes something like this where I use the DbContext and use Where statements.
public int GetBySpecification(InvoiceSpecification specification)
{
IQueryable<Invoice> query = BuildQuery(specification);
return query.Count();
}
public IQueryable<Invoice> BuildQuery(InvoiceSpecification specification)
{
IQueryable<Creditor> query = _db.Creditors;
if (!string.IsNullOrWhiteSpace(specification.Query))
{
var search = specification.Query.ToLower().Trim();
query = query.Where(c => c.OfficeEmail.Contains(search)
|| c.OfficePhone.Contains(search)
|| c.CompanyRegistrationNumber.Contains(search)
|| c.CompanyName.Contains(search)
|| c.LastName.Contains(search)
|| c.FirstName.Contains(search));
}
if (!string.IsNullOrWhiteSpace(specification.CompanyRegistrationNumber))
{
var search = specification.CompanyRegistrationNumber.ToLower().Trim();
query = query.Where(c => c.CompanyRegistrationNumber == search);
}
if (specification.UpdateFrequency.HasValue)
{
query = query.Where(c => c.UpdateFrequency == specification.UpdateFrequency.Value);
}
return query.Where(c => !c.DateDeleted.HasValue);
}
My question:
I would like to be able to use a SetUp when I run my classes. I would like to test my GetBySpecification and BuildQuery methods, and it's not uncommon that I use these methods in other methods.
I would love to be able to run a SetUp method, providing some "base database" in memory using C# objects I populate into a list, so when I use _db.Creditors, it returns a custom list of creditors I've setup and then use the queries on that one.
I think I'm quite far, but is not completely sure how I go on from here. I guess I need to update my Resolver / FakeDb set somehow, but I would really appreciate someone to help me in the right direction.
My Ninject Resolver:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILikvidoWebsitesApiContext>().ToProvider(new MoqContextProvider());
// other awesome stuff
}
My MoqContextProvider:
public class MoqContextProvider : Provider<ILikvidoWebsitesApiContext>
{
protected override ILikvidoWebsitesApiContext CreateInstance(IContext context)
{
var mock = new Mock<ILikvidoWebsitesApiContext>();
mock.Setup(m => m.Creditors).Returns(new FakeDbSet<Creditor>());
return mock.Object;
}
}
FakeDbSet implementation:
public class FakeDbSet<T> : DbSet<T>, IDbSet<T> where T : class
{
List<T> _data;
public FakeDbSet()
{
_data = new List<T>();
}
public override T Find(params object[] keyValues)
{
var keyProperty = typeof(T).GetProperty(
"Id",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
var result = this.SingleOrDefault(obj =>
keyProperty.GetValue(obj).ToString() == keyValues.First().ToString());
return result;
}
public override T Add(T item)
{
_data.Add(item);
// Identity incrementation flow
var prop = item.GetType().GetProperty("Id", typeof(int));
if (prop != null)
{
var value = (int)prop.GetValue(item);
if (value == 0)
{
prop.SetValue(item, _data.Max(d => (int)prop.GetValue(d)) + 1);
}
}
return item;
}
public override T Remove(T item)
{
_data.Remove(item);
return item;
}
public override T Attach(T item)
{
return null;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public override T Create()
{
return Activator.CreateInstance<T>();
}
public new TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public new List<T> Local
{
get { return _data; }
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities)
{
_data.AddRange(entities);
return _data;
}
public override IEnumerable<T> RemoveRange(IEnumerable<T> entities)
{
for (int i = entities.Count() - 1; i >= 0; i--)
{
T entity = entities.ElementAt(i);
if (_data.Contains(entity))
{
Remove(entity);
}
}
return this;
}
Type IQueryable.ElementType
{
get { return _data.AsQueryable().ElementType; }
}
Expression IQueryable.Expression
{
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}