2013-10-16 163 views
1

我試圖將測試添加到遺留代碼,並且當我開始添加代碼時,我感覺到某些錯誤。根據私有方法測試公共方法的方法

在下面的代碼,public方法RegisterChange呼籲兩個私有方法:

  1. 獲取對象存儲
  2. 存儲對象
public class ChangeService { 

    IRepository repository; 

    public ChangeService(IRepository repository){ 
     this.repository = repository; 
    } 

    public bool RegisterChange(int entityId){ 
     var entity = GetParsedEntity(entityId);  
     SaveEntity(entity); 
     return true; 
    } 

    private Entity GetParsedEntity(int id) { 
     var entity = repository.GetEntityById(id); 
     return new Entity{ Name = entity.Name }; 
    } 

    private void SaveEntity(Entity entity) { 
     repository.Save(Entity); 
    } 
} 

public class ChangeServiceFact(){ 

    [Fact] 
    public void When_valid_entity__Should_save_entity(){ 

     var mock = new Mock<IRepository>(); 
     var service = new ChangeService(mock.object); 

     var result = service.RegisterChange(0); 

     Assert.True(result); 
    } 
} 

所以,當林嘲笑庫,我不得不去檢查私有方法的代碼,以知道哪些操作來模擬。

我用這種方法看到的問題是,因爲代碼不僅測試測試主體(公共方法)而且測試私有方法,因此不清楚應該通過查看測試結果測試主題(公共方法)。

在稍後有人決定修改一個私有方法(如從GetParsedEntity拋出異常)時,測試將繼續正確傳遞,但由於此更改,客戶端代碼可能會失敗。

在這種特殊情況下,我使用C#,XUnit和Moq,但我認爲更通用的測試問題。

+0

當然,測試不會*通過,因爲當您調用'RegisterChange'時會引發異常。如果它有時*會拋出一個異常,那麼由做出更改的人來爲其添加一個測試。它應該符合公共方法的*記錄*(ahem)行爲。 –

+0

@JonSkeet,讓我們說GetParsedInt的變化不是一個突破的,而是一個新的功能,在這種情況下,舊的測試不知道這一點。我知道一個新的測試會拿起新的功能測試。所以,我的問題是「這是正常的方法嗎?」 (只是爲了說出它的名字)。謝謝。 – andymaster01

+1

這樣說:'GetParsedEntity'的代碼是否內聯到public方法中不應該影響測試。這是一個實現細節。所以,如果你沒有破壞任何東西,只需添加一個單獨測試的新功能,你最關心的是什麼?對我來說似乎很合理。 –

回答

3

我用這種方法看到的問題是,因爲代碼不僅測試測試主體(公共方法),而且測試私有方法,不清楚哪個應該是測試結果看着考試科目(公開的方法)。

測試對象你提到有不知道它的全部合同沒有明顯的效果。這裏的全部合同是什麼?提到的公共方法構造函數,它依賴於依賴。這是重要的依賴關係,與此依賴關係的交互是應該測試的。私有方法是(一如既往)實現細節 - 與單元測試無關。

話雖如此,讓我們回到合同。測試主題的實際合同是什麼(ChangeService方法)?要基於某個id從存儲庫中檢索對象,請創建不同的對象並將其保存在同一個存儲庫中。這是你的考驗。

[Fact] 
public void ChangeService_StoresNewEntityInRepository_BasedOnProvidedId() 
{ 
    const string ExpectedName = "some name"; 
    var otherEntity = new OtherEntity { Name = ExpectedName }; 
    var mock = new Mock<IRepository>(); 
    var service = new ChangeService(mock.object); 
    mock.Setup(m => m.GetEntityById(0)).Return(otherEntity); 

    service.RegisterChange(0); 

    mock.Verify(m => m.SaveEntity(It.Is<Entity>(e => e.Name == ExpectedName)); 
} 
+0

所以,即使看着'RegisterChange'方法,你沒有看到IRepository的直接使用,但是看着測試,這個類有一個模擬嗎?我認爲這是我最關心的問題。謝謝。 – andymaster01

+1

@ andymaster01:沒問題。這是特定對象工作的方式 - 它與依賴關係交互,沒有可見的結果(*返回值/狀態改變*)到外部世界。要檢查它的合同是否已經完成,你*詢問依賴關係,而不是測試對象本身。 –