c# - जटिल सिद्धांतों को[थ्योरी] पास करें




unit-testing xunit (4)

@ क्वेटज़लकोटल के उत्तर को अपडेट करने के लिए: विशेषता [PropertyData] को [MemberData] द्वारा हटा दिया गया है जो कि किसी भी स्थिर विधि, फ़ील्ड या संपत्ति का स्ट्रिंग नाम तर्क देता है जो एक IEnumerable<object[]> । (मुझे यह एक विशेष रूप से अच्छा लगता है कि एक इटरेटर विधि है जो वास्तव में एक बार परीक्षण मामलों की गणना कर सकती है, उन्हें गणना के रूप में उन्हें उपज कर सकती है।)

गणनाकर्ता द्वारा लौटाए गए अनुक्रम में प्रत्येक तत्व एक object[] और प्रत्येक सरणी एक ही लंबाई होनी चाहिए और वह लंबाई आपके परीक्षण मामले में तर्कों की संख्या होनी चाहिए (विशेषता [MemberData] साथ एनोटेटेड होना चाहिए और प्रत्येक तत्व के समान होना चाहिए संबंधित विधि पैरामीटर के रूप में टाइप करें। (या शायद वे परिवर्तनीय प्रकार हो सकते हैं, मुझे नहीं पता।)

( XUnit.net मार्च 2014 के लिए रिलीज नोट्स और उदाहरण कोड के साथ वास्तविक पैच देखें ।)

Xunit की एक अच्छी सुविधा है : आप एक Theory विशेषता के साथ एक परीक्षण बना सकते हैं और InlineData विशेषताओं में डेटा डाल सकते हैं, और InlineData कई परीक्षण उत्पन्न करेगा, और उन सभी का परीक्षण करेगा।

मुझे ऐसा कुछ करना है, लेकिन मेरी विधि के पैरामीटर 'सरल डेटा' नहीं हैं (जैसे string , int , double ), लेकिन मेरी कक्षा की एक सूची:

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

अज्ञात ऑब्जेक्ट सरणी बनाना डेटा बनाने का सबसे आसान तरीका नहीं है इसलिए मैंने अपनी प्रोजेक्ट में इस पैटर्न का उपयोग किया

पहले कुछ पुन: प्रयोज्य, साझा कक्षाओं को परिभाषित करें

//http://.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 संपत्ति अपने आप को एक हड्डी फेंकना है जब आपके कई परीक्षण मामलों में से एक विफल हो जाता है


मुझे लगता है कि तुम यहाँ गलत हो। वास्तव में xUnit Theory विशेषता का अर्थ क्या है: आप इस फ़ंक्शन-अंडर-टेस्ट को प्राप्त करने वाले पैरामीटर के रूप में विशेष / यादृच्छिक मान भेजकर इस फ़ंक्शन का परीक्षण करना चाहते हैं। इसका अर्थ यह है कि आप जो अगली विशेषता के रूप में परिभाषित करते हैं, जैसे: InlineData , PropertyData , ClassData , आदि .. उन पैरामीटर के लिए स्रोत होंगे। इसका मतलब है कि आपको उन पैरामीटर प्रदान करने के लिए स्रोत ऑब्जेक्ट बनाना चाहिए। आपके मामले में मुझे लगता है कि आपको ClassData ऑब्जेक्ट का स्रोत के रूप में उपयोग करना चाहिए। साथ ही - कृपया ध्यान दें कि ClassData प्राप्त होता है: ClassData IEnumerable<> - इसका मतलब है कि हर बार जेनरेट किए गए पैरामीटर का एक और सेट फ़ंक्शन-अंडर-टेस्ट के लिए इनकमिंग पैरामीटर के रूप में उपयोग किया जाएगा जब तक IEnumerable<> मान उत्पन्न करता है।

उदाहरण यहां: टॉम ड्यूपॉन्ट .NET

उदाहरण गलत हो सकता है - मैंने लंबे समय तक xUnit का उपयोग नहीं किया था


xxxxData में कई xxxxData विशेषताएँ हैं। उदाहरण के लिए PropertyData विशेषता देखें।

आप ऐसी संपत्ति को कार्यान्वित कर सकते हैं जो IENumerable IEnumerable<object[]> लौटाता है। object[] जो कि यह विधि उत्पन्न करती है, तब आपके [Theory] विधि के लिए एक कॉल के पैरामीटर के रूप में "अनपॅक" होगी।

एक अन्य विकल्प ClassData , जो वही काम करता है, लेकिन विभिन्न वर्गों / नामस्थानों में परीक्षणों के बीच 'जनरेटर' को आसानी से साझा करने की अनुमति देता है, और वास्तविक डेटा विधियों से 'डेटा जेनरेटर' को अलग करता है।

यहां से ये उदाहरण देखें:

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 }
            };
        }
    }
}

क्लासडाटा उदाहरण

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(); }
}






xunit.net