【问题标题】:Unit test Entity Framework using moq使用 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 ? 谢谢

【问题讨论】:

    标签: entity-framework unit-testing moq


    【解决方案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 不为空,这样它就不会引发空引用异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-25
      相关资源
      最近更新 更多