2012-11-19 19 views
3

我有多個實現相同接口的類,應該如何編寫單元測試來驗證每個類是否正確地實現了接口,並將代碼重複保持在最小值(DRY)?我應該如何爲一個接口的多個實現編寫單元測試,同時堅持DRY?

作爲我的意思的一個例子,以下是一個非常基本的庫,其中包含兩個實現IDeleterDeleter1Deleter2。兩者均通過在其關聯的IRepository上調用Delete來實施方法Delete

using Microsoft.Practices.Unity; 

namespace TestMultiple 
{ 
    public interface IRepository 
    { 
     void Delete(string id); 
    } 

    public abstract class Baseclass 
    { 
     protected abstract IRepository GenericRepository { get; } 

     public void Delete(string id) 
     { 
      GenericRepository.Delete(id); 
     } 
    } 

    public interface IDeleter 
    { 
     void Delete(string id); 
    } 

    public interface IRepository1 : IRepository 
    { 
    } 

    public abstract class RepositoryBase 
    { 
     public void Delete(string id) 
     { 
     } 
    } 

    public class Repository1 : RepositoryBase, IRepository1 
    { 
    } 

    public class Deleter1 : Baseclass, IDeleter 
    { 
     protected override IRepository GenericRepository { get { return Repository; } } 

     [Dependency] 
     public IRepository1 Repository { get; set; } 
    } 

    public interface IRepository2 : IRepository 
    { 
    } 

    public class Repository2 : RepositoryBase, IRepository2 
    { 
    } 

    public class Deleter2 : Baseclass, IDeleter 
    { 
     protected override IRepository GenericRepository { get { return Repository; } } 

     [Dependency] 
     public IRepository2 Repository { get; set; } 
    } 
} 

對於這兩個類,Deleter1Deleter2,我已經寫兩個相應的單元測試類如示於下段。測試檢查相同的行爲,即在基礎存儲庫上調用Delete。是否有更好的方法來實現IDeleter所有實現的相同測試?例如,我應該編寫一個包含通用測試方法的基類,例如TestDelete,對於TestDeleter1TestDeleter2

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Microsoft.Practices.Unity; 
using Moq; 

namespace TestMultiple.Tests 
{ 
    [TestClass] 
    public class TestDeleter1 
    { 
     [TestMethod] 
     public void TestDelete() 
     { 
      var mockRepo = new Mock<IRepository1>(); 
      var container = new UnityContainer().RegisterInstance<IRepository1>(mockRepo.Object); 
      var deleter = container.Resolve<Deleter1>(); 
      deleter.Delete("id"); 
      mockRepo.Verify(r => r.Delete("id")); 
     } 
    } 

    [TestClass] 
    public class TestDeleter2 
    { 
     [TestMethod] 
     public void TestDelete() 
     { 
      var mockRepo = new Mock<IRepository2>(); 
      var container = new UnityContainer().RegisterInstance<IRepository2>(mockRepo.Object); 
      var deleter = container.Resolve<Deleter2>(); 
      deleter.Delete("id"); 
      mockRepo.Verify(r => r.Delete("id")); 
     } 
    } 
} 

編輯: 隨意提的單元測試框架,可以幫助解決這類問題,雖然我的偏好是NUnit的。

+2

繼承?創建一個內部具有所有通用性的基類,然後從每個需要測試的具體類型中派生出來(不同的測試框架可能會使這變得更容易或不會) –

+0

(而且要清楚的是,我建議在測試類中繼承,而不是必須在實現中) –

+0

@Damien_The_Unbeliever這是我考慮的一種方法。我一直在用Python做這件事,但用那種語言,我認爲存在一些問題(測試基礎類,而不是每個IIRC子類)。你介意把你的建議寫成答案嗎? – aknuds1

回答

3

有沒有簡單的方法在框架中編寫測試我知道,斷言接口上的常見行爲。你所能做的最好的就是編寫測試和輔助方法,就好像你正在測試一個抽象類,然後將真實類型插入派生測試類。

例如,您可以創建一個DeleterTests類,它提供測試的接口:

public abstract class DeleterTests<TRepository> where TRepository : IRepository 
{ 
    [TestMethod] 
    public void TestDelete() 
    { 
     var mockRepo = new Mock<TRepository>(); 
     var container = new UnityContainer(); 

     container.RegisterInstance<TRepository>(mockRepo.Object); 

     var deleter = this.CreateDeleter(container); 

     deleter.Delete("id"); 
     mockRepo.Verify(r => r.Delete("id")); 
    } 

    protected abstract IDeleter CreateDeleter(IUnityContainer container); 
} 

然後,從這個類的東西實現IDeleter,實現抽象CreateDeleter方法,你需要繼承:

public class Deleter1Tests : DeleterTests<IRepository1> 
{ 
    protected override IDeleter CreateDeleter(IUnityContainer container) 
    { 
     return container.Resolve<Deleter1>(); 
    } 
} 

public class Deleter2Tests: DeleterTests<IRepository2> 
{ 
    protected override IDeleter CreateDeleter(IUnityContainer container) 
    { 
     return container.Resolve<Deleter2>(); 
    } 
} 

如果您需要以不同的方式構成的情況下,你可以實現任何方式的抽象CreateDeleter

+0

嗯只是fyi我不能立即得到這個在VS2013工作,因爲測試永遠不會被發現使用這個tecnique。 –

0

如果您的所有類都應該相同地實現IDeleter接口,那麼您的基類不應該是抽象的。在基類中實現IDeleter接口,這樣所有子類都從基礎繼承相同的實現。如果存在需要不同實現的邊緣情況,則該類可以覆蓋基類的實現。

+0

恕我直言,抽象基類是一種更好的方法。有些人認爲基礎班應該是抽象的,具體的班級是密封的。 Scott Meyers和.NET的設計者(參見Cwalina和Abrams的框架設計指南第二版第6.2節)就是其中之一。 –

+0

確實有可能讓基類做幾乎所有事情並測試它,但是對於這個問題,讓我們假設我希望直接測試每個實現而不是基類? – aknuds1

+0

@潘諾斯 - 我不認爲我應該將自己的想法變成一些純粹主義者的想法,即如何最好地編寫代碼(即使他們爲微軟工作),我相信在這項工作中使用最好的工具/技術。 – Kevin

1

你應該爲每個類編寫單元測試,而不是真的擔心其他的實現。如果你覺得你一次又一次地寫相同的測試,這可能是因爲你的生產代碼是不幹的 - 而不是你的測試代碼。正如其他人指出的那樣;如果comon中的不同實現有很多,那麼一些常見的抽象祖先可能是一個好主意。

相關問題