2017-08-21 92 views
0

我做了重構,如果我們的倉庫工廠,使之更加通用的,現在建立資料庫的方法看起來像這樣:嘲諷通用倉庫工廠方法

public TRepository CreateRepository<TRepository>(params object[] parameters) 
     where TRepository : class 
{ 
    if (_serviceProvider == null) 
     throw new ArgumentNullException(nameof(_serviceProvider)); 

    return ActivatorUtilities.CreateInstance<TRepository>(_serviceProvider, parameters); 
} 

在我的生產代碼,我使用它像這樣和它的作品般的魅力:

_concreteRepo = repoFactory.CreateRepository<ConcreteRepo>(); 

但是,當我試圖重構單元測試,以及我遇到困難建立工廠,這是我要做的事,但它不工作。

public class Tests 
{ 
    // Since I am using Moq I can't mock anything but abstract types thus having problems with type conversion in set up. 
    protected readonly Mock<IConcreteRepository> _concreteRepositoryMock = new Mock<IConcreteRepository>(); 
    protected readonly Mock<IRepositoryFactory> _factoryMock = new Mock<IRepositoryFactory>(); 

    [SetUp] 
    public void SetUp() 
    { 
     // If I don't cast concreteRepositoryMock compiler complains that cannot convert from abstract to concrete repository. 
     // If I cast it fails and returns null. 
     _factoryMock.Setup(f => f.CreateRepository<ConcreteRepository>()) 
      .Returns(_concreteRepositoryMock.Object as ConcreteRepository); 
    } 
} 

任何想法如何解決它?看起來像我的CreateRepository方法返回具體類型,但嘲笑我不能嘲笑我的具體存儲庫。我也無法將抽象類型傳遞到CreateRepository,因爲CreateInstance需要具體類型。

+1

不直接相關,但在前5行代碼中存在Factory,存儲庫,通用性,參數數組和ServiceProvider可能是過度工程的標誌。 – guillaume31

回答

0

其實我找到了解決這個問題的方法,我現在比較高興,但如果有其他建議出現,我很樂意聽到他們。

所以問題是,我的CreateRepository返回具體實現,而它必須返回抽象,因爲我所有的嘲笑都是抽象(因爲Moq嘲笑抽象)。因此,我修改了我的方法如下:

TInterface CreateRepository<TRepository, TInterface>(params object[] parameters) 
    where TRepository : class, TInterface where TInterface : class; 

而這保證了我的設置方法和成功後的編譯時安全。我確實覺得有點麻煩,但現在我可以忍受它。

注:你總是可以做內部類中擴展方法在檢測裝置和複製粘貼&從生產方法的代碼,這樣你就不會污染你的產品代碼,但是,這可能是,如果有人有問題試圖在不改變測試方法的情況下改變生產方法,這就是爲什麼爲了安全起見,我選擇「污染」我的生產代碼的原因。

1

我覺得你的問題是,你所期望的模仿對象:

protected readonly Mock<IConcreteRepository> _concreteRepositoryMock = new Mock<IConcreteRepository>(); 

要ConcreteRepository,這是錯誤的假設的實例。

_concreteRepositoryMock.Object is IConcreteRepository 

這應該說是 「真」,而

_concreteRepositoryMock.Object is ConcreteRepository 

這應該說是 「假」。

您必須要麼在你模擬切換您PROD代碼很樂意與抽象(IConcreteRepository)或嘲笑的最後一類

protected readonly Mock<ConcreteRepository> _concreteRepositoryMock = new Mock<ConcreteRepository>(); 

(注意,嘲弄具體類通常是不容易的 - 你需要的所有嘲笑方法是虛擬的等)。