2011-09-27 53 views
2

類型我想測試一下下面的代碼:如何使用一個模擬當代碼驗證它接收

public IEnumerable<KeyValuePair<Fact, Exception>> ValidateAll() 
{ 
    //...do something 
    var invalidFacts = GetInvalidFacts(); 
    //...do something 

    return duplicateFacts.Concat(invalidFacts); 
} 

private IEnumerable<KeyValuePair<Fact, Exception>> GetInvalidFacts() 
{ 
    var invalidFacts = Facts.Select(fact => 
    { 
     try 
     { 
      fact.Validate(); 
      return new KeyValuePair<Fact, Exception>(fact, null); 
     } 
     catch (FormatException e) 
     { 
      return new KeyValuePair<Fact, Exception>(fact, e); 
     } 
     catch (Exception e) 
     { 
      return new KeyValuePair<Fact, Exception>(fact, e); 
     } 
    }).Where(kv => kv.Value != null).ToList(); 

    return invalidFacts; 
} 

基本上測試的目的是檢查是否存在「事實」之內的所有對象的IEnumerable將調用其Validate方法。由於我不想測試這些對象中的代碼,因此已經有很多測試能夠做到這一點,我想注入一個假事實列表。我正在使用起訂量來製作假貨。

所以我的單元測試是這樣的:

[TestMethod] 
public void ValidateAll_ValidateMethodIsInvokedOnAllFacts_WhenCalled() 
{ 
    var anyFactOne = new Mock<Fact>(); //Fact is an abstract class. 

    anyFactOne.Setup(f => f.Validate()); 

    var dataWarehouseFacts = new DataWarehouseFacts { Facts = new Fact[] { anyFactOne.Object, FactGenerationHelper.GenerateRandomFact<SourceDetails>() } }; 

    dataWarehouseFacts.ValidateAll(); 
} 

現在,我發現了一個異常,因爲代碼實際上是驗證一種可以注射到DataWarehouseFacts類,像這樣的事實:

public IEnumerable<Fact> Facts 
{ 
    get 
    { 
     ..... 
    } 
    set 
    { 
     var allowedTypes = new [] 
     { 
      typeof(ProductUnitFact), 
      typeof(FailureFact), 
      typeof(DefectFact), 
      typeof(ProcessRunFact), 
      typeof(CustomerFact), 
      typeof(ProductUnitReturnFact), 
      typeof(ShipmentFact), 
      typeof(EventFact), 
      typeof(ComponentUnitFact), 
      typeof(SourceDetails) 
     }; 

    if(!value.All(rootFact => allowedTypes.Contains(rootFact.GetType()))) 
     throw new Exception ("DataWarehouseFacts can only be set with root facts"); 

    ProductUnitFacts = value.OfType<ProductUnitFact>().ToList(); 
    FailureFacts = value.OfType<FailureFact>().ToList(); 
    DefectFacts = value.OfType<DefectFact>().ToList(); 
    ProcessRunFacts = value.OfType<ProcessRunFact>().ToList(); 
    CustomerFacts = value.OfType<CustomerFact>().ToList(); 
    ProductUnitReturnFacts = value.OfType<ProductUnitReturnFact>().ToList(); 
    ShipmentFacts = value.OfType<ShipmentFact>().ToList(); 
    EventFacts = value.OfType<EventFact>().ToList(); 
    ComponentUnitFacts = value.OfType<ComponentUnitFact>().ToList(); 
    SourceDetails = value.OfType<SourceDetails>().Single(); 
    } 
} 

什麼是避開此驗證的最佳方法?

謝謝。

回答

0

首先,我要感謝兩個lanticge(我給了他的回答+1),併爲他們的答案se se。儘管它不是我正在尋找的東西,但他們是有意思的想法。

我不能只是將Fact類添加到允許的類型列表中,因爲這會打開許多​​不應允許的類的門;大約有30個類從它繼承。

所以我落得這樣做是爲了提取從事實屬性的設置部位的代碼在自己的方法,並讓他們的一個受保護的虛擬,像這樣:

public IEnumerable<Fact> Facts 
     { 
      get 
      { 
       ... 
      } 
      set 
      { 
       ValidateReceived(value); 
       ExtractFactTypesFrom(value.ToList()); 
      } 
     } 

     protected virtual void ValidateReceived(IEnumerable<Fact> factTypes) 
     { 
      if (factTypes == null) throw new ArgumentNullException("factTypes"); 

      var allowedTypes = GetAllowedFactTypes(); 
      if (!factTypes.All(rootFact => allowedTypes.Contains(rootFact.GetType()))) throw new Exception("DataWarehouseFacts can only be set with root facts"); 
     } 

     private IEnumerable<Type> GetAllowedFactTypes() 
     { 
      var allowedTypes = new[] 
      { 
       typeof (ProductUnitFact), 
       typeof (SequenceRunFact), 
       typeof (FailureFact), 
       typeof (DefectFact), 
       typeof (ProcessRunFact), 
       typeof (CustomerFact), 
       typeof (ProductUnitReturnFact), 
       typeof (ShipmentFact), 
       typeof (EventFact), 
       typeof (ComponentUnitFact), 
       typeof (SourceDetails) 
      }; 

      return allowedTypes; 
     } 

     private void ExtractFactTypesFrom(List<Fact> value) 
     { 
      ProductUnitFacts = value.OfType<ProductUnitFact>().ToList(); 
      FailureFacts = value.OfType<FailureFact>().ToList(); 
      DefectFacts = value.OfType<DefectFact>().ToList(); 
      ProcessRunFacts = value.OfType<ProcessRunFact>().ToList(); 
      SequenceRunFacts = value.OfType<SequenceRunFact>().ToList(); 
      CustomerFacts = value.OfType<CustomerFact>().ToList(); 
      ProductUnitReturnFacts = value.OfType<ProductUnitReturnFact>().ToList(); 
      ShipmentFacts = value.OfType<ShipmentFact>().ToList(); 
      EventFacts = value.OfType<EventFact>().ToList(); 
      ComponentUnitFacts = value.OfType<ComponentUnitFact>().ToList(); 
      SourceDetails = value.OfType<SourceDetails>().Single(); 
     } 

這樣,我就能夠創建DataWarehouseFactsForTest和覆蓋ValidateReceived方法,因此不會做任何事情:

public class DataWarehouseFactsForTests : DataWarehouseFacts 
{ 
    protected override void ValidateReceived(IEnumerable<Fact> factTypes) 
    {} 
} 

這樣,我就能夠用最小起訂量來創建事實和私營GetInvalidFacts方法中的驗證碼。例如:

[TestMethod] 
public void ValidateAll_ReturnsADictionaryWithAFormatException_WhenOneOfTheFactsValidationThrowsAFormatException() 
{ 
    var anyFactOne = new Mock<ProductUnitFact>(); 
    var anyFactTwo = new Mock<SequenceRunFact>(); 
    var anyFactThree = new Mock<SourceDetails>(); 

    anyFactOne.Setup(f => f.Validate()).Throws(new FormatException()); 

    var dataWarehouseFacts = new DataWarehouseFactsForTests { Facts = new Fact[] { anyFactOne.Object, anyFactTwo.Object, anyFactThree.Object } }; 

    var result = dataWarehouseFacts.ValidateAll().ToList(); 

    anyFactOne.Verify(f => f.Validate(), Times.Exactly(1)); 
    anyFactTwo.Verify(f => f.Validate(), Times.Exactly(1)); 
    anyFactThree.Verify(f => f.Validate(), Times.Exactly(1)); 

    Assert.AreEqual(1, result.Count()); 
    Assert.AreEqual(typeof(FormatException), result.First().Value.GetType()); 
} 
2

是飛躍在腦海中的兩個明顯的方法是:

  1. 添加Fact到允許的類型的列表。
  2. Moq您允許的事實類型之一,而不是基類Fact類本身。 (我相信,你的Validate()方法是覆寫投放。)

另一個稍微複雜一點的辦法是在測試時注入你的允許類型的列表中,假設你有過DataWarehouseFacts類控制。這可能是這個樣子:

class DWF 
{ 
    static IEnumerable<Fact> defaultAllowedFacts = new Fact[] { ... } 
    IEnumerable<Fact> allowedFacts; 

    public DWF() : this(defaultAllowedFacts) { ... } 
    internal DWF(IEnumerable<Fact> allowed) 
    { 
     // for testing only, perhaps 
     this.allowedFacts = allowed; 
    } 
    ... 
} 

然後,只需刪除該var allowedTypes = new []位和使用this.allowedFacts代替。

1

我將利用Type.IsAssignableFrom

例如而不是說

allowedTypes.Contains(v.GetType()) 

我想說

allowedTypes.Any(t => t.IsAssignableFrom(v.GetType())) 

這樣,你可以通過適當的子類一樣好,精確匹配的類型。 也許,也許,這就是你對類型列表本身的看法?

相關問題