2014-02-28 148 views
55

Xunit has a nice feature:您可以創建一個測試,其中包含Theory屬性,並將數據放入InlineData屬性中,xUnit將生成許多測試,並對其進行全部測試。將複雜參數傳遞給[Theory] ​​

我想有這樣的事情,但參數我的方法不是「簡單的數據」(如stringintdouble),但我的類的列表:

public static void WriteReportsToMemoryStream(
    IEnumerable<MyCustomClass> listReport, 
    MemoryStream ms, 
    StreamWriter writer) { ... } 
+1

如果它在你的環境中是有意義的,你可以在F#中做到這一點,噪音少得多: - http://stackoverflow.com/a/35127997/11635 –

回答

91

有許多XUnit中的xxxxData屬性。請查看例如PropertyData屬性。

您可以實施返回IEnumerable<object[]>的財產。此方法生成的object[]中的每一個都將被「解包」,作爲對您的[Theory]方法的單個調用的參數。

另一種選擇是ClassData,它的工作原理是相同的,但它允許在不同的類/名稱空間中的測試之間輕鬆共享「生成器」,並將「數據生成器」與實際測試方法分開。

見即these examples from here

PropertyData例

public class StringTests2 
{ 
    [Theory, PropertyData(nameof(SplitCountData))] 
    public void SplitCount(string input, int expectedCount) 
    { 
     var actualCount = input.Split(' ').Count(); 
     Assert.Equal(expectedCount, actualCount); 
    } 

    public static IEnumerable<object[]> SplitCountData 
    { 
     get 
     { 
      // Or this could read from a file. :) 
      return new[] 
      { 
       new object[] { "xUnit", 1 }, 
       new object[] { "is fun", 2 }, 
       new object[] { "to test with", 3 } 
      }; 
     } 
    } 
} 

ClassData例

public class StringTests3 
{ 
    [Theory, ClassData(typeof(IndexOfData))] 
    public void IndexOf(string input, char letter, int expected) 
    { 
     var actual = input.IndexOf(letter); 
     Assert.Equal(expected, actual); 
    } 
} 

public class IndexOfData : IEnumerable<object[]> 
{ 
    private readonly List<object[]> _data = new List<object[]> 
    { 
     new object[] { "hello world", 'w', 6 }, 
     new object[] { "goodnight moon", 'w', -1 } 
    }; 

    public IEnumerator<object[]> GetEnumerator() 
    { return _data.GetEnumerator(); } 

    IEnumerator IEnumerable.GetEnumerator() 
    { return GetEnumerator(); } 
} 
+0

你可以粘貼例在這裏?以防萬一鏈接破裂。 – dcastro

+0

@dcastro:是的,我實際上是尋找一些原始的xunit文檔 – quetzalcoatl

+0

嗯..沒有找到任何。那麼我會使用這些。 – quetzalcoatl

0

我猜你這裏錯了。 xUnit Theory屬性實際上意味着什麼:您希望通過發送特定/隨機值作爲此被測函數接收的參數來測試此函數。這意味着您定義的下一個屬性,如:InlineDataPropertyDataClassData等將成爲這些參數的來源。這意味着你應該構造源對象來提供這些參數。在你的情況下,我想你應該使用ClassData對象作爲源。另請注意,ClassData繼承於:IEnumerable<> - 這意味着每次生成的另一組參數都將用作待測功能的傳入參數,直到IEnumerable<>產生值。

這裏舉例:Tom DuPont .NET

例可能不正確 - 我沒有使用的xUnit很長一段時間

26

要更新@羽蛇神的回答是:[PropertyData]已被取代由[MemberData]這需要作爲參數的屬性任何返回IEnumerable<object[]>的靜態方法,字段或屬性的字符串名稱。 (我覺得特別好的,有一個iterator方法,實際上可以計算測試用例一次一個,高產起來,因爲他們正在計算。)

通過枚舉返回序列中的每個元素是一個object[]並且每個數組的長度必須相同,並且長度必須是測試用例的參數數量(用屬性[MemberData]註釋,並且每個元素必須與相應的方法參數具有相同的類型(或者它們可以是可轉換類型,我不知道。)

(見release notes for xUnit.net March 2014the actual patch with example code

+0

輕微更正......你的意思是'[MemberData]'?在任何一個鏈接中我都沒有看到有關[MethodData]的任何信息。 – rkyser

+0

@rkyser糟糕。我會解決的。 – davidbak

2

你可以試試這個方法:

public class TestClass { 

    bool isSaturday(DateTime dt) 
    { 
     string day = dt.DayOfWeek.ToString(); 
     return (day == "Saturday"); 
    } 

    [Theory] 
    [MemberData("IsSaturdayIndex", MemberType = typeof(TestCase))] 
    public void test(int i) 
    { 
     // parse test case 
     var input = TestCase.IsSaturdayTestCase[i]; 
     DateTime dt = (DateTime)input[0]; 
     bool expected = (bool)input[1]; 

     // test 
     bool result = isSaturday(dt); 
     result.Should().Be(expected); 
    } 
} 

再創建一個類來保存測試數據:

public class TestCase 
{ 
    public static readonly List<object[]> IsSaturdayTestCase = new List<object[]> 
    { 
     new object[]{new DateTime(2016,1,23),true}, 
     new object[]{new DateTime(2016,1,24),false} 
    }; 

    public static IEnumerable<object[]> IsSaturdayIndex 
    { 
     get 
     { 
     List<object[]> tmp = new List<object[]>(); 
      for (int i = 0; i < IsSaturdayTestCase.Count; i++) 
       tmp.Add(new object[] { i }); 
     return tmp; 
     } 
    } 
} 
6

創建匿名對象數組被不是構建數據的最簡單方法,所以我在我的項目中使用了這種模式

首先定義了一些可重複使用,共享類現在

//http://stackoverflow.com/questions/22093843 
public interface ITheoryDatum 
{ 
    object[] ToParameterArray(); 
} 

public abstract class TheoryDatum : ITheoryDatum 
{ 
    public abstract object[] ToParameterArray(); 

    public static ITheoryDatum Factory<TSystemUnderTest, TExecptedOutput>(TSystemUnderTest sut, TExecptedOutput expectedOutput, string description) 
    { 
     var datum= new TheoryDatum<TSystemUnderTest, TExecptedOutput>(); 
     datum.SystemUnderTest = sut; 
     datum.Description = description; 
     datum.ExpectedOutput = expectedOutput; 
     return datum; 
    } 
} 

public class TheoryDatum<TSystemUnderTest, TExecptedOutput> : TheoryDatum 
{ 
    public TSystemUnderTest SystemUnderTest { get; set; } 

    public string Description { get; set; } 

    public TExecptedOutput ExpectedOutput { get; set; } 

    public override object[] ToParameterArray() 
    { 
     var output = new object[3]; 
     output[0] = SystemUnderTest; 
     output[1] = ExpectedOutput; 
     output[2] = Description; 
     return output; 
    } 

} 

您的測試和成員的數據更容易編寫和清潔...

public class IngredientTests : TestBase 
{ 
    [Theory] 
    [MemberData(nameof(IsValidData))] 
    public void IsValid(Ingredient ingredient, string testDescription, bool expectedResult) 
    { 
     Assert.True(ingredient.IsValid == expectedResult, testDescription); 
    } 

    public static IEnumerable<object[]> IsValidData 
    { 
     get 
     { 
      var food = new Food(); 
      var quantity = new Quantity(); 
      var data= new List<ITheoryDatum>(); 

      data.Add(TheoryDatum.Factory(new Ingredient { Food = food }      , false, "Quantity missing")); 
      data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity }    , false, "Food missing")); 
      data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity, Food = food } , true, "Valid")); 

      return data.ConvertAll(d => d.ToParameterArray()); 
     } 
    } 
} 

字符串Description屬性是你扔骨頭時你的許多測試案例之一失敗

+0

我喜歡這個;它對於一個非常複雜的對象有一些真正的潛力,我必須驗證90多個屬性的驗證。我可以傳入一個簡單的JSON對象,反序列化它,併爲測試迭代生成數據。做得好。 – Gustyn