我試圖測試SystemService.cs中的GetSystem(int id)方法返回正確的值,但似乎無法弄清楚如何讓所有內容一起發揮得很好。似乎無論我做什麼, GetSystem()總是返回null。這是使用Entity Framework 6.如果我將GetSystem的主體更改為_context.Systems.SingleOrDefault(s => s.Id = id) ,那麼一切正常,但我真的想使用Find()

測試這個的正確方法是什麼?我在這個例子中使用xUnit和Moq。 SystemServiceTests.cs顯示我正在使用的代碼無效。

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。一些代碼,特別是MockableDbSetWithIQueryable ,可以在http://msdn.microsoft.com/en-US/data/dn314429找到。

一般承認的答案

我能夠找到使用Entity Framework 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);
    }
}

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()返回null因為這是System的默認值。該集合不包含id為id的項目。

.Find()List一種方法。

我建議你使用LINQ的FirstOrDefault()

原因是,您可以通過返回IQueryable來使用延遲加載




許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因