Sto cercando di verificare che il metodo GetSystem(int id)
in SystemService.cs
restituisca il valore corretto, ma non riesco a capire come ottenere tutto per giocare bene insieme. Sembra che non importa cosa faccio, GetSystem()
restituisce sempre null. Questo sta usando Entity Framework 6. Se cambio il corpo di GetSystem per leggere _context.Systems.SingleOrDefault(s => s.Id = id)
, allora tutto funziona correttamente, ma mi piacerebbe davvero usare Find()
.
Qual è il modo corretto per testarlo? In questo esempio sto usando xUnit e Moq. SystemServiceTests.cs
mostra il codice che sto attualmente utilizzando che non funziona.
SystemService.cs
namespace MyProject.Services
{
public class SystemService
{
private readonly MyContext _context;
public SystemService(MyContext context)
{
_context = context;
}
public Models.System GetSystem(int id)
{
return _context.Systems.Find(id);
}
}
}
SystemServiceTests.cs
namespace MyProject.Tests.Unit
{
public class SystemServiceTests
{
[Fact]
public void GetSystemReturnsFromContext()
{
var data = new List<Models.System> {
new Models.System { Id = 1, Name = "test 1" },
new Models.System { Id = 2, Name = "test 2" }
}.AsQueryable();
var mockContext = new Mock<MyContext>();
var mockSet = new Mock<MockableDbSetWithIQueryable<Models.System>>();
mockContext.Setup(c => c.Systems).Returns(mockSet.Object);
mockSet.Setup(m => m.Provider).Returns(data.Provider);
mockSet.Setup(m => m.Expression).Returns(data.Expression);
mockSet.Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var service = new SystemService(mockContext.Object);
var system = service.GetSystem(1);
Assert.NotNull(system); // This is always null
}
}
}
MyContext.cs
namespace MyProject.Models
{
public class MyContext : DbContext
{
public MyContext()
: base("DefaultConnection")
{
}
public virtual DbSet<Models.System> Systems { get; set; }
}
}
System.cs
namespace MyProject.Models
{
public class System
{
public int Id { get; set; }
public string Name { get; set; }
}
}
MockableDbSetWithIQueryable.cs
namespace MyProject.Tests.Helpers
{
public abstract class MockableDbSetWithIQueryable<T> : DbSet<T>, IQueryable
where T : class
{
public abstract IEnumerator<T> GetEnumerator();
public abstract Expression Expression { get; }
public abstract Type ElementType { get; }
public abstract IQueryProvider Provider { get; }
}
}
PS. Parte del codice per questo, in particolare MockableDbSetWithIQueryable
stato trovato su http://msdn.microsoft.com/en-US/data/dn314429
Sono stato in grado di trovare il modo consigliato per testare tutto utilizzando Entity Framework 6. La risorsa per questo consiglio è disponibile all'indirizzo http://msdn.microsoft.com/en-US/data/dn314431 .
In poche parole, è necessario creare classi di test per ogni bit che deve essere testato. Quello che ho finito è il seguente:
TestDbSet.cs
public class TestDbSet<TEntity> : DbSet<TEntity>, IQueryable, IEnumerable<TEntity>
where TEntity : class
{
ObservableCollection<TEntity> _data;
IQueryable _query;
public TestDbSet()
{
_data = new ObservableCollection<TEntity>();
_query = _data.AsQueryable();
}
public override TEntity Add(TEntity item)
{
_data.Add(item);
return item;
}
public override TEntity Remove(TEntity item)
{
_data.Remove(item);
return item;
}
public override TEntity Attach(TEntity item)
{
_data.Add(item);
return item;
}
public override TEntity Create()
{
return Activator.CreateInstance<TEntity>();
}
public override TDerivedEntity Create<TDerivedEntity>()
{
return Activator.CreateInstance<TDerivedEntity>();
}
public override ObservableCollection<TEntity> Local
{
get
{
return _data;
}
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
TestSystemDbSet.cs
class TestSystemDbSet : TestDbSet<Models.System>
{
public override Models.System Find(params object[] keyValues)
{
var id = (int)keyValues.Single();
return this.SingleOrDefault(s => s.Id == id);
}
}
TestContext.cs
public class TestContext: IContext
{
public TestContext()
{
this.Systems = new TestSystemDbSet();
}
public DbSet<Models.System> Systems { get; set; }
public int SaveChangesCount { get; private set; }
public int SaveChanges()
{
this.SaveChangesCount++;
return 1;
}
}
SystemServiceTests.cs
public class SystemServiceTests
{
[Fact]
public void GetSystemReturnsFromContext()
{
var context = new TestContext();
context.Systems.Add(new Models.System { Id = 1, Name = "System 1" });
context.Systems.Add(new Models.System { Id = 2, Name = "System 2" });
context.Systems.Add(new Models.System { Id = 3, Name = "System 3" });
var service = new SystemService(context);
var system = service.GetSystem(2);
Assert.NotNull(system);
Assert.Equal(2, system.Id);
Assert.Equal("System 2", system.Name);
}
}
SystemService.cs
public class SystemService : ISystemService
{
private readonly IContext _context;
public SystemService(IContext context)
{
_context = context;
}
public Models.System AddSystem(Models.System system)
{
var s = _context.Systems.Add(system);
_context.SaveChanges();
return s;
}
public Models.System GetSystem(int id)
{
return _context.Systems.Find(id);
}
}
ISystemService.cs
public interface ISystemService
{
Models.System AddSystem(Models.System system);
Models.System GetSystem(int id);
}
.Find()
restituisce null
perché questo è il valore predefinito per System
. La collezione non contiene un oggetto con id id
.
.Find()
è un metodo di List
.
Ti suggerisco di usare LINQ's FirstOrDefault()
Il motivo è che puoi utilizzare il caricamento lazy restituendo un IQueryable