2016-02-01 109 views
1

所以我是新來的TDD,這裏是我的問題:驗證虛擬方法(MOQ)

我有一個類有幾種方法。這樣的事情:

public class CompanyService : ICompanyService 
{ 
    public ICompanyRepository companyRepository; 
    public CompanyService() : this(new CompanyRepository()) 
    {     
    } 

    public CompanyService(ICompanyRepository repository) 
    { 
     companyRepository = repository; 
    } 


    public virtual bool InsertCompany(Company company) 
    { 
     return companyRepository.InsertCompany(company); 
    } 

    public bool InsertCompany(Company company, int total) 
    { 
     if (AddTotals(total)) 
     { 
      return this.InsertCompany(company); 
     } 

     return false; 
    } 


    /// <summary> 
    /// Wrapper for the static method at static service 
    /// </summary> 
    /// <param name="total"></param> 
    /// <returns></returns> 
    public virtual bool AddTotals(int total) 
    { 
     return StaticService.AddTotals(total); 
    } 
} 

我的大部分測試對這個類運行良好。因此,這裏是我的單元測試:

[TestMethod] 
    public void Test_Unit_AddTotals() 
    { 
     var service = new CompanyService(); 
     Assert.IsFalse(service.AddTotals(1)); 
    } 


    [TestMethod] 
    public void Test_Unit_InsertCompany_IsExecuted() 
    { 
     Guid id = GenerateCustomerID(); 
     var company = new Company { CustomerID = id, CompanyName = "CFN-" + id }; 
     var mock = new Mock<CompanyService>(); 
     mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true); 
     mock.Object.InsertCompany(company, 1); 
     mock.Verify(t => t.InsertCompany(It.IsAny<Company>()),Times.Once); 
    } 

    [TestMethod] 
    public void Test_Unit_InsertCompany_IsSuccess() 
    { 
     Guid id = GenerateCustomerID(); 
     var company = new Company { CustomerID = id, CompanyName = "CFN-" + id }; 
     var mock = new Mock<CompanyService>(); 
     mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true); 
     mock.Setup(t => t.InsertCompany(It.IsAny<Company>(), It.IsAny<int>())).Returns(true); 
     Assert.IsTrue(mock.Object.InsertCompany(company, 1)); 
    } 

    [TestMethod] 
    public void Test_Unit_StaticService() 
    { 
     var rep = new Mock<ICompanyRepository>(); 
     rep.Setup(t => t.InsertCompany(It.IsAny<Company>())).Returns(true); 

     var serviceMock = new Mock<ICompanyService>(); 
     serviceMock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true); 

     Assert.IsTrue(serviceMock.Object.AddTotals(0)); 
    } 

    private Guid GenerateCustomerID() 
    { 
     return Guid.NewGuid(); 
    } 

所以,當我把我的2參數InsertCompany方法虛我IsExecuted方法失敗的驗證,如果我讓非虛,那麼我不能嘲笑它IsSuccess方法它失敗了..

你能告訴我什麼我缺少你的TDD專業知識嗎?

+3

你通常不會嘲笑你正在測試的課程。你會嘲笑它依賴的東西,在這個例子中是CompanyRepository。 – dbugger

回答

0

正如評論中提到的那樣,你幾乎肯定不會測試正確的東西。看看這個測試:

[TestMethod] 
public void Test_Unit_InsertCompany_IsSuccess() 
{ 
    Guid id = GenerateCustomerID(); 
    var company = new Company { CustomerID = id, CompanyName = "CFN-" + id }; 
    var mock = new Mock<CompanyService>(); 
    mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true); 
    mock.Setup(t => t.InsertCompany(It.IsAny<Company>(), It.IsAny<int>())).Returns(true); 
    Assert.IsTrue(mock.Object.InsertCompany(company, 1)); 
} 

如果你的類被標記爲安裝程序執行正確的虛擬狀態,那麼你實際上沒有測試任何類的都沒有。當你調用一個方法時,你設置了Mock返回true,然後聲明當你調用方法時,模擬返回true ...你只是測試你已經正確設置了你的Mocks。

當您將該方法標記爲虛擬時,IsExecuted方法中使用的模擬失敗,因爲您在被測試的類中調用虛擬方法。如果你不告訴它,Moq會假設如果你正在做一個部分模擬,你只需要調用虛擬虛擬,而不是現有的實現。通過設置CallBase標誌,可以通過告訴Moq調用您現有的實現來覆蓋此內容。然後

您的測試會變成這樣:

[Test] 
public void Test_Unit_InsertCompany_IsExecuted() { 
    Guid id = GenerateCustomerID(); 
    var company = new Company { CustomerID = id, CompanyName = "CFN-" + id }; 
    var mock = new Mock<CompanyService>(); 
    mock.CallBase = true; 
    mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true); 
    mock.Setup(t => t.InsertCompany(It.IsAny<Company>())); 
    mock.Object.InsertCompany(company, 1); 
    mock.Verify(t => t.InsertCompany(It.IsAny<Company>()), Times.Once); 
} 

有兩件事情需要注意的是

  1. 在上述試驗中,CallBase = true用於執行現有的代碼。
  2. 由於1,有必要爲要驗證的調用執行Setup,否則調用將實際執行底層代碼並調用到存儲庫。

測試一個方法在同一個類中調用另一個方法並不是一個好主意。它可能會變得非常混亂,並且很難知道您期望發生的事情實際上是發生了什麼,並且將您的實現與測試代碼緊密耦合。

您已經設置了您的公司類以允許注入存儲庫。測試適當的存儲庫交互發生會減少與公司類實現的耦合,並可能導致更直接的嘲諷。

使用這種方法,您IsExecuted測試可能會成爲:

[Test] 
public void Test_Unit_InsertCompany_SavesToRepository() { 
    Guid id = GenerateCustomerID(); 
    var repoMock = new Mock<ICompanyRepository>(); 
    var company = new Company { CustomerID = id, CompanyName = "CFN-" + id }; 
    var mock = new Mock<CompanyService>(repoMock.Object); 
    mock.CallBase = true; 
    mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true); 
    mock.Object.InsertCompany(company, 1); 
    repoMock.Verify(t => t.InsertCompany(It.IsAny<Company>()), Times.Once); 
} 

理想情況下,你可以嘲笑與StaticService.AddTotals的互動,這樣你就可以實例化一個實際CompanyService,而而不是測試中的模擬對象,但是這似乎超出了當前問題的範圍。

+0

感謝您的好評。正如你所看到的,我是一個全面的TDD新手。我同意我應該將存儲庫模擬傳遞給已經注入的服務。我正在嘗試的是確保線的每個代碼覆蓋VS中的代碼覆蓋範圍,我想我有點誇張了.. 如何嘲笑與StaticService.AddTotals的交互以瞭解其他人和我的知識過程?謝謝 –

+0

@TDDNewbie如果不使用TypeMock之類的東西,靜態方法調用就不能被模擬,所以如果我想,我會考慮修改結構。我會問爲什麼它是靜態的,考慮將其包裝在一個接口中,如果有必要,可以將其作爲單例使用,以便在需要時可以注入/重寫,例如http://stackoverflow.com/a/10939204/592182工作。根據該方法的作用,您可能會決定將該依賴項添加到您的測試中,並傳遞適當的數據以修改其行爲。最好的方法真的取決於你現有的代碼和方法.. – forsvarir