c# - Moq、抽象类和虚拟属性

标签 c# .net unit-testing mocking moq

我正在使用 Moq 框架进行 Mocking。

我有一个 SocialWorker 派生自 abstract DataWorker 类。

SocialWorker 引用了几个Repositories 作为虚拟属性

我正在尝试运行以下测试:

private Place _place;
private Mock<IRepository<Resources.Data.Place>> _placeRepositoryMock;
private Mock<SocialWorker> _socialWorkerMock;

[SetUp]
public void SetUp()
{
    _place = new Place {Name = "A"};

    _socialWorkerMock = new Mock<SocialWorker> {DefaultValue = DefaultValue.Mock};

    IRepository<Resources.Data.Place> placeRepository = _socialWorkerMock.Object.PlaceRepository;

    _placeRepositoryMock = Mock.Get(placeRepository);

    _placeRepositoryMock.Setup(
        repository =>
        repository.Find(It.IsAny<Expression<Func<Resources.Data.Place, bool>>>(), null, null))
        .Returns(new[] {new Resources.Data.Place()});
}

[Test]
public void AddShouldAddANewPlace()
{
    var placeManager = new PlaceManager(_socialWorkerMock.Object);

    object placeEntity = placeManager.Add(_place);

    placeEntity.GetType().Should().Equal(typeof (Resources.Data.Place));

    _socialWorkerMock.Verify(
        socialWorker => socialWorker.PlaceRepository.Add(It.IsAny<Resources.Data.Place>()), Times.Once());

    _placeRepositoryMock.Verify(
        placeRepository =>
        placeRepository.Find(p => p.Name.Equals(_place.Name), null, null).First(),
        Times.Once());
}

此测试的最后一次验证失败并出现此错误:

System.NotSupportedException : Invalid verify on a non-virtual (overridable in VB) member: placeRepository => placeRepository.Find(p => p.Name.Equals(._place.Name), null, null).First<Place>()

总的来说,我对最小起订量和单元测试还很陌生。

下面是相关代码供大家引用:

IUnitOfWork

public interface IUnitOfWork : IDisposable
{
    void CommitChanges();
}

IRepository

public interface IRepository<T>
{
    void Add(T entity);

    void Delete(T entity);

    IEnumerable<T> Find(Expression<Func<T, bool>> filter = null,
                        Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                        IList<string> includedProperties = null);

    T FindById(object id);

    void Update(T entity);
}

DataWorker

public abstract class DataWorker : IUnitOfWork
{
    protected ObjectContext ObjectContext;
    private bool _disposed;

    protected DataWorker()
    {
        ObjectContext = new ObjectContext(ConfigurationManager.ConnectionStrings["DataEntities"].ConnectionString);
    }

    ~DataWorker()
    {
        . . .
    }

    protected virtual void Dispose(bool disposing)
    {
        . . .
    }

    public virtual void CommitChanges()
    {
        ObjectContext.SaveChanges();
    }

    public void Dispose()
    {
        . . .
    }
}

数据存储库

public class DataRepository<T> : IRepository<T> where T : EntityObject
{
    private readonly ObjectSet<T> _objectSet;

    public DataRepository(ObjectContext objectContext)
    {
            _objectSet = objectContext.CreateObjectSet<T>();
    }

    public void Add(T entity)
    {
        . . .
    }

    public void Delete(T entity)
    {
        . . .
    }

    public virtual IEnumerable<T> Find(Expression<Func<T, bool>> filter = null,
                               Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
                               IList<string> includedProperties = null)
    {
        . . .
    }

    public T FindById(object id)
    {
        . . .
    }


    public void Update(T entity)
    {
        . . .
    }
}

社工

public class SocialWorker : DataWorker
{
    private IRepository<ContactRequest> _contactRequestRepository;
    private IRepository<Place> _placeRepository;
    private IRepository<User> _userRepository;

    public virtual IRepository<ContactRequest> ContactRequestRepository
    {
        get
        {
            return _contactRequestRepository ??
                   (_contactRequestRepository = new DataRepository<ContactRequest>(ObjectContext));
        }
    }

    public virtual IRepository<Place> PlaceRepository
    {
        get { return _placeRepository ?? (_placeRepository = new DataRepository<Place>(ObjectContext)); }
    }

    public virtual IRepository<User> UserRepository
    {
        get { return _userRepository ?? (_userRepository = new DataRepository<User>(ObjectContext)); }
    }
}

最佳答案

我对你的代码试图做什么感到困惑,设计似乎有点奇怪。

首先,您遇到问题的测试是引用一个类 PlaceManager,您没有指定它,因为它是测试的一部分,所以很难看出哪里出了问题。

此外,我不确定 PlaceManager 的用途。您的设计有 SocialWorker,其属性提供 PlaceRepository,那么 PlaceManager 的用途是什么,为什么要将它传递给 SocialWorker SocialWorker 不管理自己的 PlaceRepository 吗?

为什么不调用 _socialWorker.PlaceRepository.Add(place),或者更简洁地调用 _socialWorker.Add(place)

我只是猜测 PlaceManger 访问了 SocialWorker.PlaceRepository ,但我不明白为什么 SocialWorker.PlaceRepository 是一个公共(public)属性(property),没有什么可以阻止任何人直接使用并忽略 PlaceManager。如果 PlaceManager 也是 Place 的某种Repository

此外,您的 IRepository 代码将 Add() 作为空值,但 PlaceManager 返回一个 对象 .为什么这不是 Place 的强类型?看起来这就是您所期望的...并且您的 PlaceManager.Add() 方法返回一个 Place,尽管 Repository Add() 方法是 void。 (在它被持久化到上下文之后它是同一个地方吗?)您的 PlaceManager 是否正在对 Place 做其他事情?

也许我的想法有误?您能否更清楚地说明您的代码的意图是什么,而不是它的作用?抱歉问了这么多问题,但我想知道您的设计应该做什么,然后再弄清楚您为什么要针对非虚拟...进行验证...

我认为具体问题的原因是,在您的验证中,您期望针对 Find 进行验证,但是,这包括调用 First(),它需要被模拟,但不能被模拟,因为它是静态扩展方法,不是虚拟的或抽象的。

无论如何,我不确定您为什么要反对 First()。您正在验证的方法只是:

_placeRepositoryMock.Verify(placeRepository =>
   placeRepository.Find(p => p.Name.Equals(_place.Name), null, null),
   Times.Once());

这就是实际调用的内容,因为这是存储库中方法的签名。只能检查接口(interface)方法是否被调用。

关于c# - Moq、抽象类和虚拟属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10050168/

相关文章:

c# - 模拟/ stub 系统资源(如 System.Threading.Mutex)的首选方法

javascript - 如何在 Breeze 中对扩展实体使用谓词

c# - 使用 EEPlus 读取 Excel 文件时,Excel 日期字段值与 C# dateTime 相差 1 天

c# - XAML 样式/基类中 VisualStateGroup/VisualState 的继承

c# - 带有锁定文件的 FileStream

c# - 如何将 DbSet 转换为 ObservableCollection

c# - 优雅的解决方案 - 使用 Linq-To-SQL 批量删除和重新添加相关的子列表

c# - 求解具有约束结果的最小二乘矩阵

javascript - Karma 单元测试 : Module name "react" has not been loaded yet for context: _. 使用 require([])

python - 以某种方式使用 py.test 时,我可以使用 python 调试器进行调试吗?