【问题标题】:Moq, abstract class and virtual propertiesMoq、抽象类和虚拟属性
【发布时间】:2012-04-06 22:58:01
【问题描述】:

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

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

SocialWorker 引用了一对Repositories 作为virtual property

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

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()
    {
        . . .
    }
}

DataRepository

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)
    {
        . . .
    }
}

SocialWorker

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

【问题讨论】:

    标签: c# .net unit-testing mocking moq


    【解决方案1】:

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

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

    此外,我不确定 PlaceManager 的用途。你的设计有SocialWorker 和一个提供PlaceRepository 的属性,那么PlaceManager 是什么,为什么它被传递一个SocialWorkerSocialWorker不是自己管理PlaceRepository吗?

    你为什么不打电话给_socialWorker.PlaceRepository.Add(place),或者更确切地说是_socialWorker.Add(place)

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

    此外,您的IRepository 代码将Add() 设为无效,但PlaceManager 返回object。为什么这不是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());
    

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

    【讨论】:

    • 这是一个 MVC 应用程序。我可以直接从我的控制器调用 _socialWorker.PlaceRepository.Add(place)。但我更喜欢让我的控制器尽可能薄。此外,PlaceManager 所做的不仅仅是普通的CRUD。例如,从 Yahoo! 的 PlaceFinder 服务中查找相关地点。再加上PlaceManager 的存在,因此将来,服务/API 模块也可以访问相同的代码位,而无需直接针对UnitWorkerRepository 编写代码。
    • 我仍然不确定PlaceManagerPlaceRepository 之间的区别是什么。什么时候可以直接访问PlaceRepository,什么时候通过PlaceManager(毕竟,没有什么可以阻止任何人忽略PlaceManager)。为什么PlaceManager 不对PlaceRepository 负责?这对我来说真的像是一种气味,它似乎没有遵循 SRP(SocialWorker 做的事情太多了,Places 的责任是分开的)。 Places 至少可以加2个地方,整体的设计理念我不懂,看糊涂了。
    • 我的控制器甚至不知道存在工作人员或存储库。他们只能访问一个经理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 2012-01-31
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    相关资源
    最近更新 更多