使用 moq 对实体框架进行单元测试
Posted
技术标签:
【中文标题】使用 moq 对实体框架进行单元测试【英文标题】:Unit test Entity Framework using moq 【发布时间】:2012-10-19 09:57:53 【问题描述】:我正在使用实体框架并尝试对使用 EF 的数据服务进行单元测试。 我没有使用存储库和工作单元模式。 我尝试了以下方法来模拟上下文和 DbSet:
private static Mock<IEFModel> context;
private static Mock<IDbSet<CountryCode>> idbSet;
[ClassInitialize]
public static void Initialize(TestContext testContext)
context = new Mock<IEFModel>();
idbSet = new Mock<IDbSet<CountryCode>>();
context.Setup(c => c.CountryCodes).Returns(idbSet.Object);
对于 idbSet“本地”,我得到空的“对象引用未设置为对象的实例”错误。 有没有办法像这样模拟 idbSet ? 谢谢
【问题讨论】:
【参考方案1】:我是这样计算的: 创建了两个名为 DbSetMock 的类:
public class DbSetMock<T> : IDbSet<T>
where T : class
#region Fields
/// <summary>The _container.</summary>
private readonly IList<T> _container = new List<T>();
#endregion
#region Public Properties
/// <summary>Gets the element type.</summary>
public Type ElementType
get
return typeof(T);
/// <summary>Gets the expression.</summary>
public Expression Expression
get
return this._container.AsQueryable().Expression;
/// <summary>Gets the local.</summary>
public ObservableCollection<T> Local
get
return new ObservableCollection<T>(this._container);
/// <summary>Gets the provider.</summary>
public IQueryProvider Provider
get
return this._container.AsQueryable().Provider;
#endregion
#region Public Methods and Operators
/// <summary>The add.</summary>
/// <param name="entity">The entity.</param>
/// <returns>The <see cref="T"/>.</returns>
public T Add(T entity)
this._container.Add(entity);
return entity;
/// <summary>The attach.</summary>
/// <param name="entity">The entity.</param>
/// <returns>The <see cref="T"/>.</returns>
public T Attach(T entity)
this._container.Add(entity);
return entity;
/// <summary>The create.</summary>
/// <typeparam name="TDerivedEntity"></typeparam>
/// <returns>The <see cref="TDerivedEntity"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
throw new NotImplementedException();
/// <summary>The create.</summary>
/// <returns>The <see cref="T"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public T Create()
throw new NotImplementedException();
/// <summary>The find.</summary>
/// <param name="keyValues">The key values.</param>
/// <returns>The <see cref="T"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public T Find(params object[] keyValues)
throw new NotImplementedException();
/// <summary>The get enumerator.</summary>
/// <returns>The <see cref="IEnumerator"/>.</returns>
public IEnumerator<T> GetEnumerator()
return this._container.GetEnumerator();
/// <summary>The remove.</summary>
/// <param name="entity">The entity.</param>
/// <returns>The <see cref="T"/>.</returns>
public T Remove(T entity)
this._container.Remove(entity);
return entity;
#endregion
#region Explicit Interface Methods
/// <summary>The get enumerator.</summary>
/// <returns>The <see cref="IEnumerator"/>.</returns>
IEnumerator IEnumerable.GetEnumerator()
return this._container.GetEnumerator();
#endregion
和 EFModelMock:
public class EFModelMock : IEFModel
#region Fields
/// <summary>The country codes.</summary>
private IDbSet<CountryCode> countryCodes;
#endregion
#region Public Properties
/// <summary>Gets the country codes.</summary>
public IDbSet<CountryCode> CountryCodes
get
this.CreateCountryCodes();
return this.countryCodes;
#endregion
#region Public Methods and Operators
/// <summary>The commit.</summary>
/// <exception cref="NotImplementedException"></exception>
public void Commit()
throw new NotImplementedException();
/// <summary>The set.</summary>
/// <typeparam name="T"></typeparam>
/// <returns>The <see cref="IDbSet"/>.</returns>
/// <exception cref="NotImplementedException"></exception>
public IDbSet<T> Set<T>() where T : class
throw new NotImplementedException();
#endregion
#region Methods
/// <summary>The create country codes.</summary>
private void CreateCountryCodes()
if (this.countryCodes == null)
this.countryCodes = new DbSetMock<CountryCode>();
this.countryCodes.Add(
new CountryCode CountryName = "Australia", DisplayLevel = 2, TelephoneCode = "61" );
#endregion
然后像这样测试:
[TestClass]
public class CountryCodeServiceTest
#region Static Fields
/// <summary>The context.</summary>
private static IEFModel context;
#endregion
#region Public Methods and Operators
/// <summary>The initialize.</summary>
/// <param name="testContext">The test context.</param>
[ClassInitialize]
public static void Initialize(TestContext testContext)
context = new EFModelMock();
/// <summary>The country code service get country codes returns correct data.</summary>
[TestMethod]
public void CountryCodeServiceGetCountryCodesReturnsCorrectData()
// Arrange
var target = new CountryCodeService(context);
var countryName = "Australia";
var expected = context.CountryCodes.ToList();
// Act
var actual = target.GetCountryCodes();
// Assert
Assert.IsNotNull(actual);
Assert.AreEqual(actual.FirstOrDefault(a => a.CountryName == countryName).PhoneCode, expected.FirstOrDefault(a => a.CountryName == countryName).TelephoneCode);
【讨论】:
你...这个问题/答案需要更多的声誉。我看到很多人都有这个问题。我希望我不必走这条路,但你已经完成了繁重的工作:) 对不起,但是……这是什么接口:IEFModelMock?【参考方案2】:您必须设置idbSet
模拟的Local
属性。
例如:
idbSet = new Mock<IDbSet<CountryCode>>();
var col = new ObservableCollection<CountryCode>();
idbSet.SetupGet(x => x.Local).Returns(col);
【讨论】:
感谢您的回答,但是没有用,我终于手动模拟了IDbSet和IEFModel。【参考方案3】:我一直在使用一种方法来创建我的模拟集,如下所示:
public static Mock<IDbSet<T>> CreateMockSet<T>(IQueryable<T> data) where T : class
var mockSet = new Mock<IDbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
return mockSet;
我只是添加了这一行:
mockSet.Setup(x => x.Local).Returns(new ObservableCollection<T>());
return 语句之前,它解决了我的问题。
我的许多查询如下所示:
var myset = context.EntitySetName.Local.SingleOrDefault(x=>x.something==something)
??
context.SingleOrDefault(x=>x.something==something);
所以我只需要 Local 不为空,这样它就不会引发空引用异常。
【讨论】:
以上是关于使用 moq 对实体框架进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章
.net测试篇之单元测试/集成测试神器Autofixture