[তত্ত্ব] এ জটিল পরামিতিগুলি পাস করুন


100

জুনিতের একটি দুর্দান্ত বৈশিষ্ট্য রয়েছে : আপনি একটি Theoryগুণাবলীর সাহায্যে একটি পরীক্ষা তৈরি করতে পারেন এবং বৈশিষ্ট্যগুলিতে ডেটা রাখতে পারেন InlineDataএবং xUnit অনেকগুলি পরীক্ষা উত্পন্ন করে এবং সেগুলি সমস্ত পরীক্ষা করে।

আমি ভালো কিছু করতে চান, কিন্তু আমার পদ্ধতি প্যারামিটার না 'সহজ ডাটা' (মত string, int, double), কিন্তু আমার ক্লাসের একটি তালিকা:

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

উত্তর:


139

xxxxDataএক্স ইউনাইটে অনেকগুলি বৈশিষ্ট্য রয়েছে। PropertyDataবৈশিষ্ট্য উদাহরণস্বরূপ দেখুন ।

যে সম্পত্তি প্রত্যাবর্তন করে আপনি তা প্রয়োগ করতে পারেন IEnumerable<object[]>object[]এই পদ্ধতিটি যে প্রতিটি উত্পন্ন করে তা আপনার [Theory]পদ্ধতিতে একক কল করার জন্য প্যারামিটার হিসাবে "প্যাক করা" হবে ।

আরেকটি বিকল্প হ'ল ClassData, এটি একই কাজ করে তবে বিভিন্ন শ্রেণি / নেমস্পেসের পরীক্ষার মধ্যে সহজেই 'জেনারেটরগুলি' ভাগ করে দেয় এবং বাস্তব পরীক্ষার পদ্ধতি থেকে 'ডেটা জেনারেটর'কে পৃথক করে।

এই উদাহরণগুলি দেখুন এখানে থেকে :

প্রপার্টিডাটা উদাহরণ

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

@ ডকাস্ট্রো: হ্যাঁ, আমি আসলে কিছু মূল জুনিত ডক্সে অনুসন্ধান করছি
কোয়েটজলকোটল

4
@Nick: আমি একমত যে, PropertyData অনুরূপ, কিন্তু, আপনি এটি জন্য কারণ নির্দিষ্ট করেছেন: static। ঠিক এই কারণেই আমি করতাম না। ক্লাসডেটা হ'ল আপনি যখন স্ট্যাটিক্স থেকে পালাতে চান। এটি করে আপনি জেনারেটরগুলিকে সহজেই পুনরায় ব্যবহার করতে পারবেন (অর্থাত্ নীড়)।
কোয়েটজলকোটল

4
কোনও ধারণা ক্লাসডাটা নিয়ে কী ঘটেছে? আমি এটি xUnit2.0 এ সন্ধান করতে পারি না, আপাতত, আমি স্ট্যাটিক পদ্ধতিতে মেম্বার ডেটা ব্যবহার করছি, যা শ্রেণীর নতুন উদাহরণ তৈরি করে, এবং এটি ফেরত দেয়।
এর্তি-ক্রিস ইলমা

14
@ ইরতি, বৈশিষ্ট্য [MemberData("{static member}", MemberType = typeof(MyClass))]প্রতিস্থাপন করতে ব্যবহার করুন ClassData
জুনে লি

7
সি # 6 হিসাবে nameofকোনও সম্পত্তির নাম হার্ডকডিংয়ের পরিবর্তে কীওয়ার্ডটি ব্যবহার করার পরামর্শ দেওয়া হয়েছিল (সহজেই নীরবে ভাঙ্গা যায়)।
সারা

41

@ কোয়েটজলকোটলের উত্তর আপডেট করতে: এই বৈশিষ্ট্যটি ছাড়িয়ে [PropertyData]দেওয়া হয়েছে [MemberData]যা কোনও স্থিতিশীল পদ্ধতি, ক্ষেত্র বা সম্পত্তি হিসাবে ফিরে আসে এমন স্ট্রিংয়ের নাম হিসাবে যুক্তি হিসাবে গ্রহণ করে IEnumerable<object[]>। (আমার কাছে পুনরুক্তি পদ্ধতিটি বিশেষত চমৎকার লাগছে যা পরীক্ষার কেসগুলি একবারে গণনা করতে পারে , সেগুলি গণনা করার সাথে সাথেই উপার্জন করতে পারে))

গণকের দ্বারা প্রাপ্ত অনুক্রমের প্রতিটি উপাদান একটি object[]এবং প্রতিটি অ্যারে অবশ্যই একই দৈর্ঘ্য এবং সেই দৈর্ঘ্যটি অবশ্যই আপনার পরীক্ষার ক্ষেত্রে আর্গুমেন্টের সংখ্যা হতে হবে (বৈশিষ্ট্যের সাথে বর্ণিত [MemberData]এবং প্রতিটি উপাদানটির অবশ্যই সংশ্লিষ্ট পদ্ধতি প্যারামিটারের মতো একই ধরণের থাকতে হবে) । (অথবা তারা রূপান্তরযোগ্য ধরণের হতে পারে, আমি জানি না))

( XUnit.net মার্চ 2014 এর রিলিজ নোট এবং উদাহরণ কোড সহ প্রকৃত প্যাচ দেখুন ))


4
@ ডেভিডবাক কডপ্লেক্স চলে গেছে। লিঙ্কটি কাজ করছে না
কিশান বৈষ্ণব

10

মনে করুন যে আমাদের একটি জটিল কার শ্রেণি রয়েছে যার একটি উত্পাদনকারী শ্রেণি রয়েছে:

public class Car
{
     public int Id { get; set; }
     public long Price { get; set; }
     public Manufacturer Manufacturer { get; set; }
}
public class Manufacturer
{
    public string Name { get; set; }
    public string Country { get; set; }
}

আমরা একটি থিওরি পরীক্ষায় কার ক্লাসটি পূরণ এবং পাস করতে যাচ্ছি।

সুতরাং এমন একটি 'কারক্লাসডেটা' শ্রেণি তৈরি করুন যা নীচের মতো কার শ্রেণীর উদাহরণ দেয়:

public class CarClassData : IEnumerable<object[]>
    {
        public IEnumerator<object[]> GetEnumerator()
        {
            yield return new object[] {
                new Car
                {
                  Id=1,
                  Price=36000000,
                  Manufacturer = new Manufacturer
                  {
                    Country="country",
                    Name="name"
                  }
                }
            };
        }
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

এটি একটি পরীক্ষার পদ্ধতি (কার্টেস্ট) তৈরি করার এবং গাড়ীটিকে প্যারামিটার হিসাবে সংজ্ঞায়নের সময় এসেছে:

[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(Car car)
{
     var output = car;
     var result = _myRepository.BuyCar(car);
}

তত্ত্ব জটিল

শুভকামনা


4
এই উত্তরটি থিওরি ইনপুট হিসাবে একটি কাস্টম ধরণের পাস করার প্রশ্নকে স্পষ্টভাবে সম্বোধন করে যা নির্বাচিত উত্তরটি অনুপস্থিত বলে মনে হয়।
জেডি কেইন

4
এটি হ'ল আমি ব্যবহার-কেসটি খুঁজছিলাম যা কোনও থিওরিতে প্যারামিটার হিসাবে কোনও জটিল টাইপ কীভাবে পাস করতে হয়। পুরোপুরি কাজ করে! এটি এমভিপি নিদর্শনগুলি পরীক্ষা করার জন্য সত্যই অর্থ প্রদান করে। আমি এখন সব ধরণের রাজ্যে একটি ভিউর বিভিন্ন দৃষ্টান্ত স্থাপন করতে পারি এবং সেগুলি একই থিয়রিতে পাস করতে পারি যা উপস্থাপক পদ্ধতিগুলির সেই দৃষ্টিভঙ্গির প্রভাবগুলির পরীক্ষা করে। এটা ভালবাসা!
ডেনিস এম রান্নাঘর

10

বেনামে অবজেক্ট অ্যারে তৈরি করা ডেটা তৈরির সহজতম উপায় নয় তাই আমি এই প্রকল্পটি আমার প্রকল্পে ব্যবহার করেছি

প্রথমে কিছু পুনরায় ব্যবহারযোগ্য, ভাগ করা ক্লাস নির্ধারণ করুন

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

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

    public static ITheoryDatum Factory<TSystemUnderTest, TExpectedOutput>(TSystemUnderTest sut, TExpectedOutput expectedOutput, string description)
    {
        var datum= new TheoryDatum<TSystemUnderTest, TExpectedOutput>();
        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 TExpectedOutput 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, bool expectedResult, string testDescription)
    {
        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সম্পত্তি হ'ল নিজেকে হাড় ফেলে দেওয়া যখন আপনার অনেক পরীক্ষার ক্ষেত্রে ব্যর্থ হয় throw


4
আমি এটি পছন্দ করি; এটি একটি খুব জটিল অবজেক্টের জন্য কিছু বাস্তব সম্ভাবনা রয়েছে আমাকে 90+ বৈশিষ্ট্যে বৈধতা যাচাই করতে হবে। আমি একটি সাধারণ JSON অবজেক্টে পাস করতে পারি, এটি ডিসরিয়ালাইজ করতে পারি এবং পরীক্ষার পুনরাবৃত্তির জন্য ডেটা উত্পন্ন করতে পারি। সাবাশ.
গুস্টাইন

4
ইসভালিড টেস্টমেডোথের প্যারামিটারগুলি কি মিশ্রিত নয় - এটি ইসভালিড (উপাদান, এক্সপ্রেসটেড রেজাল্ট, টেস্টডেস্ক্রিপশন) হওয়া উচিত না?
পাস্তাকুল

3

আপনি এইভাবে চেষ্টা করতে পারেন:

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

1

আমার প্রয়োজনের জন্য আমি কয়েকটি পরীক্ষার মাধ্যমে 'পরীক্ষা ব্যবহারকারীদের' সিরিজ চালাতে চেয়েছিলাম - তবে [ক্লাসডাটা] ইত্যাদি আমার যা প্রয়োজন তার চেয়ে ওভারকিল বলে মনে হয়েছিল (কারণ প্রতিটি পরীক্ষায় আইটেমের তালিকা স্থানীয়করণ করা হয়েছিল)।

সুতরাং আমি পরীক্ষাগুলির ভিতরে একটি অ্যারের সাথে নিম্নলিখিতটি করলাম - বাইরে থেকে সূচিযুক্ত:

[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task Account_ExistingUser_CorrectPassword(int userIndex)
{
    // DIFFERENT INPUT DATA (static fake users on class)
    var user = new[]
    {
        EXISTING_USER_NO_MAPPING,
        EXISTING_USER_MAPPING_TO_DIFFERENT_EXISTING_USER,
        EXISTING_USER_MAPPING_TO_SAME_USER,
        NEW_USER

    } [userIndex];

    var response = await Analyze(new CreateOrLoginMsgIn
    {
        Username = user.Username,
        Password = user.Password
    });

    // expected result (using ExpectedObjects)
    new CreateOrLoginResult
    {
        AccessGrantedTo = user.Username

    }.ToExpectedObject().ShouldEqual(response);
}

এটি আমার লক্ষ্য অর্জন করেছিল, পরীক্ষার উদ্দেশ্য পরিষ্কার রেখে clear আপনাকে কেবল সূচীগুলি সিঙ্কে রাখা দরকার তবে এগুলি সবই।

ফলাফলগুলিতে দেখতে দুর্দান্ত লাগছে, এটি সঙ্কুচিত হতে পারে এবং কোনও ত্রুটি পেলে আপনি একটি নির্দিষ্ট উদাহরণটি আবার চালু করতে পারেন:

এখানে চিত্র বর্ণনা লিখুন


"ফলাফলগুলিতে দেখতে দুর্দান্ত লাগছে, এটি সঙ্কুচিত হতে পারে এবং ত্রুটি পেলে আপনি একটি নির্দিষ্ট উদাহরণটি আবার চালু করতে পারেন"। খুব ভাল পয়েন্ট। এর একটি বড় অসুবিধা হ'ল MemberDataমনে হচ্ছে আপনি নির্দিষ্ট পরীক্ষা ইনপুট দিয়ে পরীক্ষাটি দেখতে বা চালাতে পারবেন না। এটি স্তন্যপান।
অলিভার পিয়ারমাইন

আসলে, আমি সবেমাত্র কাজ করেছি যে MemberDataআপনি TheoryDataএবং optionচ্ছিকভাবে ব্যবহার করলে এটি সম্ভব IXunitSerializable। আরও তথ্য এবং এখানে এক্সম্যাপলস ... github.com/xunit/xunit/issues/429#issuecomment-108187109
অলিভার

1

এইভাবেই আমি আপনার সমস্যার সমাধান করেছি, আমারও একই অবস্থা ছিল। সুতরাং প্রতিটি রানে কাস্টম অবজেক্ট এবং বিভিন্ন সংখ্যক অবজেক্টের সাথে ইনলাইন করুন।

    [Theory]
    [ClassData(typeof(DeviceTelemetryTestData))]
    public async Task ProcessDeviceTelemetries_TypicalDeserialization_NoErrorAsync(params DeviceTelemetry[] expected)
    {
        // Arrange
        var timeStamp = DateTimeOffset.UtcNow;

        mockInflux.Setup(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>())).ReturnsAsync("Success");

        // Act
        var actual = await MessageProcessingTelemetry.ProcessTelemetry(JsonConvert.SerializeObject(expected), mockInflux.Object);

        // Assert
        mockInflux.Verify(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>()), Times.Once);
        Assert.Equal("Success", actual);
    }

সুতরাং এটি আমার ইউনিট পরীক্ষা, প্যারামগুলির প্যারামিটারটি লক্ষ্য করুন । এটি বিভিন্ন সংখ্যা অবজেক্ট পাঠাতে দেয় allow এবং এখন আমার ডিভাইস টেলমেট্রি টেস্টডাটা ক্লাস:

    public class DeviceTelemetryTestData : IEnumerable<object[]>
    {
        public IEnumerator<object[]> GetEnumerator()
        {
            yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
            yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
            yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
            yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

আশা করি এটা সাহায্য করবে !


-1

আমার ধারণা আপনি এখানে ভুল করেছেন। এক্স ইউনাইট Theoryঅ্যাট্রিবিউটটির অর্থ কী : আপনি এই ফাংশনটির পরীক্ষা-নিরীক্ষার মাধ্যমে প্যারামিটার হিসাবে বিশেষ / র্যান্ডম মান প্রেরণ করে পরীক্ষা করতে চান। তার মানে আপনি যেমন পরবর্তী অ্যাট্রিবিউট, যেমন কি সংজ্ঞায়িত: InlineData, PropertyData, ClassData, ইত্যাদি .. যারা পরামিতি জন্য উৎস হতে হবে। এর অর্থ এই যে পরামিতিগুলি সরবরাহ করতে আপনার উত্স অবজেক্টটি তৈরি করা উচিত। আপনার ক্ষেত্রে আমি অনুমান করি আপনার ClassDataউত্সটি উত্স হিসাবে ব্যবহার করা উচিত । এছাড়াও - দয়া করে নোট করুন যে এর ClassDataথেকে প্রাপ্ত: IEnumerable<>- এর অর্থ প্রতিবার উত্পাদিত প্যারামিটারগুলির একটি সেট অন্য IEnumerable<>মানগুলি তৈরি না করা পর্যন্ত ফাংশন-আন্ডার-টেস্টের জন্য আগত প্যারামিটার হিসাবে ব্যবহৃত হবে ।

উদাহরণ এখানে: টম ডুপন্ট। নেট

উদাহরণটি ভুল হতে পারে - আমি দীর্ঘদিন ধরে xUnit ব্যবহার করিনি

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.