Método de búsqueda de Entity Framework Find

asp.net-web-api c# entity-framework entity-framework-6 unit-testing

Pregunta

Estoy intentando probar que el GetSystem(int id) en SystemService.cs devuelve el valor correcto, pero parece que no puedo encontrar la manera de que todo funcione bien juntos. Parece que no importa lo que haga, GetSystem() siempre devuelve null. Esto está utilizando Entity Framework 6. Si cambio el cuerpo de GetSystem para leer _context.Systems.SingleOrDefault(s => s.Id = id) , entonces todo funciona correctamente, pero realmente me gustaría usar Find() .

¿Cuál es la forma correcta de probar esto? Estoy usando xUnit y Moq en este ejemplo. SystemServiceTests.cs muestra el código que estoy usando actualmente que no funciona.

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; }
  }
}

PD. Parte del código para esto, específicamente MockableDbSetWithIQueryable se encontró en http://msdn.microsoft.com/en-US/data/dn314429

Respuesta aceptada

Pude encontrar la forma recomendada de probar todo utilizando Entity Framework 6. El recurso para esta recomendación está disponible en http://msdn.microsoft.com/en-US/data/dn314431 .

En pocas palabras, las clases de prueba deben crearse para cada bit que se debe probar. Lo que acabé haciendo es lo siguiente:

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);
}

Respuesta popular

.Find() devuelve un null porque ese es el valor predeterminado para el System . La colección no contiene un elemento con id id .

.Find() es un método de List .

Te sugiero que uses FirstOrDefault() de LINQ FirstOrDefault()

La razón es que puede usar la carga perezosa devolviendo un IQueryable



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué