2011-11-09 58 views
5

在我們,我們結束了一個共享的測試夾具這給了很多的問題,我們的單元測試的最後一個項目。所以在我們目前的項目中,我已經看到了構建器模式。我們在開發機器上的內存中運行單元測試,並在構建服務器上運行數據庫。如何有效地創建和使用生成器模式

目前我有一個學生產生例如以下製造商T4模板:

public class StudentBuilder : Builder<Student, StudentBuilder> 
{ 
    public StudentBuilder() 
    { 
     IsMale = true; 
    } 

    public StudentBuilder WithFirstName(string firstName) 
    { 
     this.FirstName = firstName; 
     return this; 
    } 

    public StudentBuilder WithLastName(string lastName) 
    { 
     this.LastName = lastName; 
     return this; 
    } 

    public StudentBuilder WithIsMale(bool isMale) 
    { 
     this.IsMale = isMale; 
     return this; 
    } 

    internal override Student Construct() 
    { 
     Student result = new Student() 
     { 
      FirstName = FirstName ?? "FirstName:" + id.ToString(), 
      LastName = LastName ?? "LastName:" + id.ToString(), 
      IsMale = IsMale, 
      Id = id, 
     }; 

    / return result; 
    } 
} 

海槽基類,我可以以下列方式使用:

Student wouter = StudentBuilder.Build() 
    .WithFirstName("Wouter") 
    .WithLastName("de Kort"); 
List<Student> students = StudentBuilder.Build().Multiple(10, (builder, index) => builder.WithFirstName("FirstName" + index)); 

我們運行在我們的構建服務器上進行集成測試以確保所有內容對數據庫都有效這意味着我們必須確保滿足所有參考約束條件。 但是隨後問題就開始了。

例如,一個學生需要有一個導師,導師所屬的一所學校,一所學校的一個城市,一個城市一個....

這將導致類似的代碼:

StudentBuilder.Build().WithMentor(MentorBuilder.Build().WithSchool(SchoolBuilder.Build().WithCity(CityBuilder.Build())) 

我應該如何優化呢?我想過在每個生成器的構建方法做了「默認的建設」,但如果我將建立10個學生,然後它會導致10個導師在10所學校的10個城市中10 ....

也許創造一個像WithAllCity(..),在一切的(學校)

任何想法的方法呢?我是否正確地使用Builder模式? Director類能幫助嗎?還是應該從StudentBuilder繼承類來解決這些不同的情況?

或者另一個想法,我應該將數據發送到數據庫之前在我的業務層添加更多的驗證?然後,我會在單元測試中發現內存數據庫中的更多錯誤。

+0

'我們在內存上開發的機器和反對在構建server.'數據庫運行單元測試 - 這聽起來很痛苦的解決。你的項目如何運作? –

+0

我使用http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5創建具有統一部分的不同app.config設置,以便從內存切換到數據庫。開發人員可以讓單元測試通過切換構建配置來定位數據庫。這加快了開發速度(在內存測試中運行速度非常快),但也捕獲了內存虛假不能捕獲的錯誤。但是在那裏你立刻就會有這個問題,爲什麼我問這個問題。一個非常長的構建器設置,以確保測試通過構建服務器 –

回答

1

如果你的單元測試將要使用學生的導師,導師的學校和學校的城市,我認爲單元測試有代碼來構建所有這些是合理的,但我建議你的單元測試可能不是隻測試一件事。使你的單元測試更加具體,以便它們不會深入到許多屬性中。

如果問題不在於你的單元測試,而是你的學生課要求導師進入其構造函數,並且導師不能爲空,考慮放寬這個要求以允許一個空導師(我的偏好我想) ,或者讓建設者像你說的那樣填寫一個「默認」對象。如果你試圖訪問它們的屬性,你甚至可以讓你的默認對象拋出異常,提示你單元測試需要你創建一個「實際」對象。

+0

問題是我們正在運行我們的單元測試也針對真正的數據庫(構建服務器上的集成測試),並且數據模型需要設置這些屬性。這也是因爲有時例如人們忘記初始化一個在SQL Server中引發錯誤但不在內存中的DateTime。 –

+1

分開您的設備和集成測試。保持單元測試(內存)特定。保持集成測試(使用數據庫)一般,並使用多個測試共享的完整數據集(我知道,這正是您試圖避免的)。在http://stackoverflow.com/questions/482827上有一些很好的建議可以讓共享測試數據更容易使用,但我認爲你不應該在集成測試中完全避免它。對不起,如果這是一個非答案:) –

+0

我測試演示者訪問存儲庫的服務。集成測試使用WCF和實體框架,因此這些測試需要很長時間才能運行。但是他們確實發現我們的單元測試本身在數據庫本身中找不到的錯誤。所以我必須進行單元測試和集成測試,我的演示者和測試代碼完全相同(我們現在已經在內存中設置了一些依賴注入和假ObjectContext)。我無法創建單獨的集成測試,因爲這將是我的單元測試的重複,但速度要慢得多(在上一個項目中使用單元測試而死亡) –

0

如果你打算建立學生名單,你可以製作一個名單建造者類 - StudentsBuilder。默認情況下,構建器類將生成一個學生列表,將由您定義的僞隨機屬性。這與AutoPoco的方法類似。

我發現讓自己的list builder類在定義創建行爲和支持任何類型的類方面更加靈活。我使用IList<T>字段(類似於數組(SoA)方法的面向數據結構)構建生成器類。

public class StudentsBuilder 
{ 
    private int _size; 
    private IList<string> _firstNames; 
    private IList<string> _lastNames; 
    private IList<MentorBuilder> _mentors; 

    public StudentsBuilder(int size = 10) 
    { 
     _size = 10; 
     _firstNames = new RandomStringGenerator(size).Generate(); 
     _lastNames = new RandomStringGenerator(size).Generate(); 
     _mentors = Enumerable.Range(0, size).Select(_ => new MentorBuilder()).ToList(); 
    } 

    public StudentsBuilder WithFirstNames(params string[] firstNames) 
    { 
     _firstNames = firstNames; 
     return this; 
    } 

    public IList<Student> Build() 
    { 
     students = new List<Student>(); 
     for (int i = 0; i < size; i++) 
      students.Add(new Student(_firstNames[i], _lastNames[i], _mentors[i].Build()); 
     return students; 
    } 
} 

每個字段列表都使用一個採用參數數組參數的單獨方法重寫。您還可以公開字段列表,以便使用更高版本的With(Action<StudentsBuilder> action)語法來覆蓋值。測試代碼如下所示:

var students = new StudentBuilder(size: 4) 
    .WithFirstNames("Jim", "John", "Jerry", "Judy") 
    .Build(); 
相關問題