2012-04-06 192 views
3

我正在使用Moq框架進行Mocking。Moq,抽象類和虛擬屬性

我有一個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>() 

我對Moq和單元測試一般都很陌生。

這裏是供您參考相關代碼:

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

回答

2

我感到困惑你的代碼試圖這樣做,設計似乎有點奇怪。

首先,您遇到問題的測試是引用PlaceManager類,您尚未指定,因爲這是測試的一部分,所以很難看清楚它出錯的位置。

此外,我不確定PlaceManager的用途。你的設計有SocialWorker與財產提供PlaceRepository,那麼什麼是PlaceManager,爲什麼它通過SocialWorker?不是SocialWorker自己管理PlaceRepository

你爲什麼不打電話_socialWorker.PlaceRepository.Add(place),或者說更加整齊?

我只是猜測,PlaceManger訪問SocialWorker.PlaceRepository,但我不明白爲什麼作爲SocialWorker.PlaceRepository是公共財產,有什麼能阻止直接使用,而忽略PlaceManager任何人。這就是如果PlaceManager也是某種RepositoryPlace

此外,您的IRepository代碼有Add()作爲void,但PlaceManager返回object。爲什麼這不是強類型到Place?這似乎是你所期望的...並且您的PlaceManager.Add()方法返回Place,但RepositoryAdd()方法爲void。 (它是堅持到上下文之後它是相同的地方嗎?)PlaceManager是否Place

也許我的棒有錯誤的結局?你能否更清楚地知道你的代碼的意圖是什麼?對不起,有很多問題,但我想知道你的設計應該做什麼之前弄清楚爲什麼你試圖驗證對非虛擬...

我認爲的具體原因問題是,在您確認您希望對Find進行驗證的情況下,其中包括對First()的調用,該調用需要進行模擬而不能進行,因爲它是一種靜態擴展方法,不是虛擬或抽象的。

無論如何,我不確定你爲什麼要打First()。您正在驗證的方法只是:

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

這就是實際調用的方法,因爲這是存儲庫中方法的簽名。您只能檢查是否調用了接口方法。

+0

這是一個MVC應用程序。我可以直接從我的控制器調用_socialWorker.PlaceRepository.Add(place)。但我更願意讓我的控制器儘可能薄。而且'PlaceManager'不僅僅是正常的'CRUD'。例如,從雅虎的'PlaceFinder'服務中找到相關的地方。此外,還有'PlaceManager',以便將來服務/ API模塊可以訪問相同的代碼位,而不必直接針對'UnitWorker'或'Repository'編寫代碼。 – Moon 2012-04-07 14:36:33

+0

我仍然不確定'PlaceManager'和'PlaceRepository'之間的區別是什麼。什麼時候會直接訪問'PlaceRepository',何時通過'PlaceManager'訪問(畢竟,沒有什麼能阻止任何人忽略'PlaceManager')。爲什麼'PlaceManager'不負責'PlaceRepository'?這真的對我來說似乎是一種嗅覺,它並沒有出現在SRP之後(SocialWorker做了太多的事情,並且'Place'的責任被分割了。 '地方'可以添加至少2個地方,我不瞭解整體設計概念,它似乎混淆了我。 – nicodemus13 2012-04-07 14:50:48

+0

我的控制器甚至不知道存在工作人員或存儲庫。他們所有的權限都是經理。 – Moon 2012-04-07 18:36:40