Reputation: 41
In a few words. Wpf app, ef used. I need to test model behavior using mock and unity. Unity and mock seem to me to be clear.
The question is following:
Model doesn't get the context through it constructor. It uses the context while execute the methods like this:
public Toys[] Get()
{
using (Context context = new Context())
{
return context.Toys.ToArray();
}
}
This is how I try to test:
[TestClass]
public class TestToyModel
{
[TestMethod]
public void TestToyCreate()
{
List<Toy> toys = new List<Toy>();
toys.Add(new Toy{ Id = "1234", Name = "Toy1" });
DbSet<Toy> dbToys = GetQueryableMockDbSet(toys);
Mock<ToyModel> model = new Mock<ToyModel>();
Mock<Context> context = new Mock<Context>();
context.Setup(x => x.Toys).Returns(dbToys);
//it' s all for now
}
private static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));
return dbSet.Object;
}
}
How can I supply my mock model with mock context ?
Upvotes: 1
Views: 1112
Reputation: 247561
The context is being created manually so the class is tightly coupled to context which makes it difficult to unit test in isolation.
context is a dependency. abstract it out so that it can be mocked and injected into the subject under test.
public interface IContext : IDisposable {
public DbSet<Toy> Toys { get; }
//...other properties and methods
}
public class Context : DbContext, IContext {
public Context() { ... }
public DbSet<Toy> Toys { get; set; }
//...other properties and methods
}
Assuming system under test
public class ToyModel {
private readonly IContext context;
public MyClass(IContext context) {
this.context = context;
}
public Toys[] Get() {
return context.Toys.ToArray();
}
public void Create(Toy toy) {
context.Toys.Add(toy);
context.SaveChanges();
}
}
The class is no longer responsible for creating the dependency. That responsibility is now passed/delegated out to another class. Also note that your classes should depend on abstractions and not on concretions. This allows for more flexibility when swapping implementations. Like mocking for unit tests.
Now the context can be mocked and injected into the dependent class. Here is a simple example based on what you have done so far.
[TestClass]
public class TestToyModel {
[TestMethod]
public void TestToyCreate() {
//Arrange
var toys = new List<Toy>();
toys.Add(new Toy { Id = "1234", Name = "Toy1" });
var dbToys = GetQueryableMockDbSet(toys); //DbSet<Toy>
var contextMock = new Mock<IContext>();
contextMock.Setup(x => x.Toys).Returns(dbToys);
var sut = new ToyModel(contextMock.Object);
//Act
sut.Create(new Toy { Id = "5678", Name = "Toy2" });
//Assert
var expected = 2;
var actual = toys.Count;
Assert.AreEqual(expected, actual);
}
//...other code removed for brevity
}
If the ToyModel
is the system under test, there is no need to mock it. Create an instance and pass the mocked dependencies to satisfy the test.
Upvotes: 1