2013-07-15 15 views
2

我有一個客戶業務實體,只能通過CustomerFactory創建。但是,對於如何對使用Customer對象的類執行單元測試,我有疑問,因爲每當使用客戶時,單元測試都需要設置Customer及其CustomerFactory。涉及通過工廠問題和注意事項創建實體的單元測試類

使用CustomerFactory而不是構造函數的原因是它需要通過ICustomerValidator進行驗證。然而,將驗證邏輯放在構造函數中需要將ICustomerValidator注入構造函數,這可能不是所期望的。例如,在執行ICustomerService的單元測試時,必須創建設置步驟,包括創建和設置Customer和CustomerFactory(加上通過ICustomerValidator進行的客戶驗證,例如IdValidFirstName)。此外,無論客戶實例何時用於單元測試,這些設置都必須重複。

下面是一個正在單元測試的ICustomerService,但低於客戶的設定必須創建:

我想單元測試這個實現

public interface ICustomerService 
    { 
     bool AddCustomer(Customer customer); 
    } 

public class Customer 
    { 
     internal Customer() 
     { } 

     public int Id { get; internal set; } 

     public string Firstname { get; internal set; } 

     public string Surname { get; internal set; } 

     public DateTime DateOfBirth { get; internal set; } 

     public string EmailAddress { get; internal set; } 

     public bool HasCreditLimit { get; internal set; } 

     public Company Company { get; internal set; } 

     //... other properties 
} 

請注意,ICustomerService的兩個實施和ICustomerFactory取決於ICustomerValidator

 public interface ICustomerFactory 
     { 
      Customer CreateCustomerWith(int id, string firstName, string surname, DateTime dateOfBirth, string emailAddress, Company company);   
     } 

public class CustomerFactory : ICustomerFactory 
    { 
     private ICustomerValidator customerValidator; 
     public CustomerFactory(ICustomerValidator customerValidator) 
     { 
      this.customerValidator = customerValidator; 
     } 

     public Customer CreateCustomerWith(int id, string firstName, string surname, DateTime dateOfBirth, 
      string emailAddress, Company company) 
     { 

     //validation logic here ... 
     } 
    } 



public interface ICustomerValidator 
    { 
     IDictionary<string, string> Errors { get; } 

     bool IsValid(Customer customer); 

     bool IsValidId(int id); 

     bool IsValidFirstName(string firstName); 

     bool IsValidSurname(string surname); 

     bool IsValidDateOfBirth(DateTime dateOfBirth); 

     bool IsValidEmailAddress(string emailAddress); 

     bool IsValidCompany(Company company); 

    } 

但我必須設置該在執行單元測試ICustomerService,whic h似乎很多設置,或者它是正確的,還是有更好的方法(我可以使它重用,但仍然必須在ICustomerService的測試中調用它,是唯一的方法)?

private Customer CreatCustomer() 
     { 
      var customerId = 0; 
      var firstName = "John"; 
      var surname = "Tom"; 
      var emailAddress = "[email protected]"; 
      var dateOfBirth = new DateTime(2013, 1, 1); 
      var company = new Company(34, "Unit", Classification.VIP); 
      SetupForCustomerCreation(customerId,firstName,surname,emailAddress,dateOfBirth,company); 

      var customer = this.customerFactory 
             .CreateCustomerWith(
              customerId, firstName, surname, dateOfBirth, emailAddress, company); 

      return customer; 
     } 

     private void SetupForCustomerCreation(int customerId, string firstName, string surname, string emailAddress, DateTime dateOfBirth, Company company) 
     { 
      this.customerValidatorMock.Setup(c => 
               c.IsValidId(customerId)).Returns(true); 
      this.customerValidatorMock.Setup(c => 
               c.IsValidFirstName(firstName)).Returns(true); 
      this.customerValidatorMock.Setup(c => 
               c.IsValidSurname(surname)).Returns(true); 
      this.customerValidatorMock.Setup(c => 
               c.IsValidDateOfBirth(dateOfBirth)).Returns(true); 
      this.customerValidatorMock.Setup(c => 
               c.IsValidEmailAddress(emailAddress)).Returns(true); 
      this.customerValidatorMock.Setup(c => 
               c.IsValidCompany(company)).Returns(true); 
     } 

在此先感謝!

+0

請告訴我爲什麼downvote?這是別人可能會遇到的問題。 – Pingpong

+0

認爲你錯過了一些代碼。什麼是customerValidatorMock,以及爲什麼需要多次運行安裝程序?附:我從來沒有低估過你。 – TheKingDave

+0

請參閱我添加CustomerFactory的更新。 customerValidatorMock用於ICustomerService和ICustomerFactor。 – Pingpong

回答

1

我會建議公開Customer構造函數。這樣做後,您可以自由使用它,而無需一直接觸其工廠。通過不向公衆推薦某個班級,就會隱含地依賴於其工廠,這在大多數情況下是不被推薦的。最好有鬆散耦合的類,因爲它更容易維護和測試

+0

使用CustomerFactory而不是構造函數的原因是它需要通過ICustomerValidator進行驗證。然而,將驗證邏輯放在構造函數中需要將ICustomerValidator注入構造函數,這可能不是所期望的。 – Pingpong

+0

如果您可以將代碼更改爲首先創建一個類,然後將其傳遞給驗證程序,如果該代碼無效並拋出異常,該怎麼辦?通過這種方式,我們的API將變得更加清潔,您不需要在驗證器中反映所有屬性,只需傳遞整個客戶對象即可。 –

0

儘管我強烈建議@Alexander-Mihalciuc回答正確的方法,您可以將客戶類定義爲InternalsVisible的程序集暴露給您的測試組件,並且能夠在測試中創建客戶類的實例,即使您保留了內部ctor也是如此。