2010-04-28 52 views
7

如何處理私有方法的單元測試?C#設計問題

我有一個類將僱員數據加載到數據庫中。下面是一個示例:

>

public class EmployeeFacade 
{ 
    public Employees EmployeeRepository = new Employees(); 
    public TaxDatas TaxRepository = new TaxDatas(); 
    public Accounts AccountRepository = new Accounts(); 
    //and so on for about 20 more repositories etc. 

    public bool LoadAllEmployeeData(Employee employee) 
    { 
     if (employee == null) 
      throw new Exception("..."); 


     bool exists = EmployeeRepository.FetchExisting(emps.Id); 
     if (!exists) 
     { 
      EmployeeRepository.AddNew(); 
     } 

     try 
     { 
      EmployeeRepository.Id = employee.Id; 
      EmployeeRepository.Name = employee.EmployeeDetails.PersonalDetails.Active.Names.FirstName; 
      EmployeeRepository.SomeOtherAttribute; 
     } 
     catch() {} 

     try 
     { 
      emps.Save(); 
     } 
     catch(){} 


     try 
     { 
      LoadorUpdateTaxData(employee.TaxData); 
     } 
     catch() {} 

     try 
     { 
     LoadorUpdateAccountData(employee.AccountData); 
     } 
     catch() {} 
     ... etc. for about 20 more other employee objects 

    } 

    private bool LoadorUpdateTaxData(employeeId, TaxData taxData) 
    { 
     if (taxData == null) 
      throw new Exception("..."); 

     ...same format as above but using AccountRepository 

    } 

    private bool LoadorUpdateAccountData(employee.TaxData) 
    { 
     ...same format as above but using TaxRepository 
    } 
} 

我寫的應用程序採取串行化對象(e.g僱員以上)和數據加載到數據庫中。

我有幾個問題的設計,我想上意見:

A - 我調用這個類「EmployeeFacade」因爲我(試圖?)使用外觀模式。命名班級名稱的模式是否很好練習?

B - 調用DAL圖層類的「具體實體」是否合適「EmployeeRepository」?

c - 表示以這種方式使用的倉庫合理或者我應該建立在庫本身的方法採取,比如說,員工,然後從那裏加載數據例如EmployeeRepository.LoadAllEmployeeData(Employee employee)?我的目標是有凝聚力的類,但是這會需要知識庫中的Employee對象可能不是很好?

D - 有沒有什麼好方法可以在每個方法開始時不必檢查對象是否爲空? E - 我有一個EmployeeRepository,TaxRepository,AccountRepository聲明爲公共單元測試目的。這些都是真正的私有實體,但我需要能夠用存根替換它們,以便不會寫入我的數據庫(我重載save()方法以便不執行任何操作)。無論如何,還是我必須暴露他們?

F - 我如何測試私有方法 - 或者完成了這件事(事情告訴我它不是)?

G- 「emps.Name = employee.EmployeeDetails.PersonalDetails.Active.Names.FirstName;」這違反了德米特法則,但我如何調整我的目標以遵守法律?

+3

7個問題? – 2010-04-28 23:20:45

+0

這是一個工作出售;) – RCIX 2010-04-28 23:22:23

+0

這可能是一個好主意,問這是7個不同的問題,而不是把它們集中在一起。 – 2010-04-28 23:22:32

回答

1

如何處理的私有方法的單元測試?

你不應該寫爲私有方法的測試。

創建私有方法的唯一可能方式是的重構已經測試公共方法。

+0

這不就是說你完全按照他的方式去做嗎?標記他們公開,測試,然後標記私人一旦測試成功? – baron 2010-04-29 01:29:11

+0

不是。你所說的在概念上是錯誤的。當你在TDD之後開發類時 - 你永遠不會得到未經測試的方法。因爲如果你做的都是正確的話 - 那麼:1.創建測試2.實現測試的方法3.獲得綠線4.重構實現的方法(在這裏你可以將方法拆分成幾個私有/受保護的方法, *在步驟1) – zerkms 2010-04-29 01:51:57

+0

是有意義的。謝謝澄清 – baron 2010-04-29 01:53:18

0

答:我不認爲在類名中使用模式名稱尤其不好,儘管我不知道它多久執行一次。 F:我認爲zerkms是正確的,你可能必須讓它們公開,測試它們,然後當你滿意時讓它們變爲私有。一旦他們私有,你仍然可以測試使用私有方法的公共方法,以確保他們繼續工作。至於你的DAL等,我會建議看看LINQ to SQL,可用於.NET 3.0及更高版本。這是處理業務邏輯和數據庫之間抽象層的一個很好的框架。這裏有幾個鏈接檢查...

Quick Tutorial for LINQ to SQL in C# Part 1 of Scott Guthrie's blog

斯科特·格思裏有很多好東西LINQ的,如果你有興趣,你應該看看他的更多信息。

+0

我其實並不是指你的回答:-)通常的私人方法**是從公共方法(已經被測試覆蓋)中提取出來的**,而不僅僅是從公開方式變爲私有方式。 – zerkms 2010-04-28 23:35:39

4

一個 - 我不會把它XXXFacade,但更有意義的事情(這實際上可能意味着你應該把它XXXFacade)

- 我會叫他們XXXRepository

C - 我在這裏並不真正瞭解你的模型 - 你傳遞一個Employee對象並將它的值賦給EmployeeRepository中的equivilent值。存儲庫不應包含數據字段 - 存儲庫的每個實例都不代表數據庫中的一行。通過對數據庫中的實體集合進行操作(即:存儲庫是表,實體是行),存儲庫是一種通過數據庫獲取數據的方式。我期望Repository對象有一個Save方法,它將一個Employee對象作爲參數,並將其保存到數據庫中。除了作爲一個Load方法這需要一個標識和回報和員工:

Employee myEmployee = repository.Load(112345); 
myEmployee.Name = "New Name"; 
repository.Save(myEmployee); 

資源庫的基礎類並不需要了解具體執行Employee類的,通過使用泛型和多態性。看看Sh#rpArchitecture就是一個很好的例子。

d - 是的,把那個普通邏輯的抽象基類(庫)

ë - 不要讓他們公開,如果他們應該是私有的。如果您需要在單元測試中使用存儲庫的邏輯來模擬提取數據,請實現一個通用接口,然後在測試中模擬該接口。您不需要測試存儲庫是否會返回正確的數據,因爲實際上數據是暫時的並且不一致。更好的僞裝它,並測試你的行爲是否符合你對模擬存儲庫中的預測數據的期望。

F - 不要。測試行爲不執行。

G - 如果您按上面所述檢查您的體系結構,我認爲不存在此問題。

+0

我正在使用EntitySpaces請參閱:http://www.developer.entityspaces.net/documentation/Entity/Create.aspx – guazz 2010-04-28 23:57:05

+0

好的 - 所以Collections(即:EmployeeCollections)是存儲庫,而Exmployee是實體。如果上面的代碼片段,假設僱員被傳遞給函數是UI中的視圖模型是否安全? 我在閱讀代碼時遇到了很多麻煩。例如,您可以調用一個變量EmployeeRepository,但該變量似乎也是一個類型: public Employees EmployeeRepository = new Employees(); EmployeeRepository emps = new EmployeeRepository(); – 2010-04-29 00:28:31

+0

對不起,這是代碼中的錯誤。我已經更新了它。是員工是從UI傳遞的視圖模型,並且必須將其加載到數據庫。 – guazz 2010-04-29 01:36:52

0

A - IMO,是的。它會立即提醒您模式,並幫助您理解代碼,這也許是代碼編寫中的重要實踐之一 - 讓其他人瞭解您的代碼。

B - 我更喜歡xxDAO約定(數據訪問對象)。我傾向於「面向服務的編程」,意思是「知道」保存員工的服務,而不是「模型」和「控制」之間混合的「存儲庫對象」。

D - 也許使用Aspect,但我不推薦它。

E - 你可以爲這些分類創建一個接口,並使用setter從外部注入它們(就像spring一樣),或者從某種工廠獲取它們。這樣,對你來說很容易用模擬替換類,並且仍然使成員保持「私密」。

F - 我認爲這些方法應該從「負載員工」方面提取出來,並且是自助服務。海事組織,你應該抽象「員工數據」對象(特別是如果你有20個:-))。並編寫一個知道加載任何類型的「員工數據對象」的簡單服務。

希望我幫助,
吉文

1

A - 我打電話該類 「EmployeeFacade」 因爲我 使用門面 模式(試圖?)。將名稱上的模式命名爲 是否很好?

我不認爲測試私有方法是一個好主意;但是,您可以測試「內部」類,它們與外部程序集無法訪問它們時的私有類似,只要將它們標記爲單元測試項目的內部可見即可。

的AssemblyInfo.cs - [assembly: InternalsVisibleTo("YourClass.Tests")]

乙 - 這是好叫我DAL層類的具體 實體 「庫」 如 「EmployeeRepository」?

我經常這樣做,我不認爲它有什麼問題。

c - 表示使用這種 方式庫明智或者我應該創建存儲庫本身 方法 取,也就是說,員工,然後加載 從那裏例如數據 EmployeeRepository.LoadAllEmployeeData(員工 員工)?我的目標是具有凝聚力的班級 ,但這需要 知識庫來了解 員工對象,這可能不太好?

除非我不明白,否則我會讓他們分開。我通常使用我的Repository類作爲簡單的CRUD幫助程序,我會在存儲庫中編寫一個包裝來展示所需的功能。

d - 有沒有解決的 不必檢查對象是否 空在每個方法的開頭任何好的方式?

如果有,我不知道,我只想用ArgumentNullException()

E - 我現在有一個EmployeeRepository, TaxRepository,AccountRepository 聲明爲public單元測試 目的。這些真的是私有的 實體,但我需要能夠 替換這些與存根,這樣 將不會寫入我的數據庫(我 超載的save()方法做 沒什麼)。無論如何圍繞這 還是我必須暴露他們?

查看我對A的回答,將它們標記爲Internal,然後將InternalsVisible設置爲您的單元測試程序集。另見MSDN

F - 如何測試私有方法 - 或者這樣做了(某事告訴我不是)?

我通常不會測試私有方法和需要測試的私有類,我將其標記爲內部並在我的測試程序集中使用它們。

+0

你能解釋爲什麼你不打擾測試私有方法嗎? – baron 2010-04-29 01:55:29

+0

他們是實現的細節 - 要麼測試是因爲公共方法已經過測試,而公共方法調用私有方法,或者如果需要的話,它們是「內部」的,並且對我的測試程序集可見。 – Nate 2010-04-29 13:57:03

0
  1. 您的命名約定似乎沒問題。

  2. 通過調用具體存儲庫,您緊密耦合系統。在構造函數中傳遞它們的repo對象。或者使用DI/IOC容器。

  3. 如果存儲庫正在返回員工,它會知道它。您可能希望回購知道員工類的合同。

  4. 如果您爲某些東西獲得空值,您應該確保提供程序代碼不發送空值。

  5. 您可以通過正確實施依賴注入並使用接口來實現該目的。

  6. 標準單元測試框架不會給你,你需要像Moles。此示例顯示爲post

  7. 如果可以的話,可以使用繼承。但是如果對象模型需要,那麼我認爲你是無助的。