测试实体框架查找方法

本文关键字:方法 查找 框架 实体 测试 | 更新日期: 2023-09-27 18:37:24

我正在尝试测试SystemService.cs中的GetSystem(int id)方法返回正确的值,但似乎无法弄清楚如何让所有内容很好地结合在一起。似乎无论我做什么,GetSystem()总是返回 null。这是使用实体框架 6。如果我更改 GetSystem 的主体以读取_context.Systems.SingleOrDefault(s => s.Id = id),那么一切正常,但我真的很想使用 Find() .

测试它的正确方法是什么?在这个例子中,我使用的是xUnit和Moq。 SystemServiceTests.cs显示了我当前正在使用的不起作用的代码。

系统服务.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);
    }
  }
}

系统服务测试.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
    }
  }
}

我的上下文.cs

namespace MyProject.Models
{
  public class MyContext : DbContext
  {
    public MyContext()
      : base("DefaultConnection")
    {
    }
    public virtual DbSet<Models.System> Systems { get; set; }
  }
}

系统.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; }
  }
}

附言。这方面的一些代码,特别是MockableDbSetWithIQueryable代码,可以在 http://msdn.microsoft.com/en-US/data/dn314429

测试实体框架查找方法

我能够找到使用实体框架 6 测试所有内容的推荐方法。有关此建议的资源,请访问 http://msdn.microsoft.com/en-US/data/dn314431。

简而言之,需要为需要测试的每个位创建测试类。我最终做的是:

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

测试上下文.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;
    }
}

系统服务测试.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);
    }
}

系统服务.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()返回null,因为这是System的默认值。该集合不包含 ID 为 id 的项。

.Find()是一种List的方法。

我建议你使用LINQ的FirstOrDefault()

原因是,您可以通过返回IQueryable来使用延迟加载