একই আচরণ প্রদর্শনকারী একাধিক অবজেক্টের জন্য আপনি ইউনিট পরীক্ষাগুলি কীভাবে গঠন করবেন?


9

অনেক ক্ষেত্রে আমার কিছু আচরণ সহ একটি বিদ্যমান বর্গ থাকতে পারে:

class Lion
{
    public void Eat(Herbivore herbivore) { ... }
}

... এবং আমার একটি ইউনিট পরীক্ষা আছে ...

[TestMethod]
public void Lion_can_eat_herbivore()
{
    var herbivore = buildHerbivoreForEating();
    var test = BuildLionForTest();
    test.Eat(herbivore);
    Assert.IsEaten(herbivore);
}

এখন, আমার সিংহের সাথে পরিচয়মূলক আচরণ সহ একটি টাইগার শ্রেণি তৈরি করা দরকার:

class Tiger
{
    public void Eat(Herbivore herbivore) { ... }
}

... এবং যেহেতু আমি একই আচরণ চাই, আমার একই পরীক্ষা চালানো দরকার, আমি এরকম কিছু করি:

interface IHerbivoreEater
{
    void Eat(Herbivore herbivore);
}

... এবং আমি আমার পরীক্ষার রিফ্যাক্টর:

[TestMethod]
public void Lion_can_eat_herbivore()
{
    IHerbivoreEater_can_eat_herbivore(BuildLionForTest);
}


public void IHerbivoreEater_can_eat_herbivore(Func<IHerbivoreEater> builder)
{
    var herbivore = buildHerbivoreForEating();
    var test = builder();
    test.Eat(herbivore);
    Assert.IsEaten(herbivore);
}

... এবং তারপরে আমি আমার নতুন Tigerক্লাসের জন্য আরও একটি পরীক্ষা যুক্ত করেছি :

[TestMethod]
public void Tiger_can_eat_herbivore()
{
    IHerbivoreEater_can_eat_herbivore(BuildTigerForTest);
}

... এবং তারপরে আমি আমার Lionএবং Tigerক্লাসগুলি (সাধারণত উত্তরাধিকার সূত্রে, তবে কখনও কখনও রচনা দ্বারা) রিফ্যাক্টর করি :

class Lion : HerbivoreEater { }
class Tiger : HerbivoreEater { }

abstract class HerbivoreEater : IHerbivoreEater
{
    public void Eat(Herbivore herbivore) { ... }
}

... এবং সব ঠিক আছে। তবে কার্যকারিতা যেহেতু এখন HerbivoreEaterক্লাসে রয়েছে তাই এখন মনে হচ্ছে প্রতিটি সাবক্লাসে এই প্রতিটি আচরণের জন্য পরীক্ষা করাতে কিছু ভুল আছে। তবুও এটি সাবক্লাসগুলি যা প্রকৃতপক্ষে গ্রাস করা হচ্ছে এবং এটি কেবলমাত্র বাস্তবায়নের বিশদ যা তারা ওভারল্যাপিং আচরণগুলি ভাগ করে নিতে পারে ( Lionsএবং Tigersউদাহরণস্বরূপ, এর সম্পূর্ণ ভিন্ন ব্যবহার থাকতে পারে)।

একই কোডটি একাধিকবার পরীক্ষা করা অযথা মনে হয়, তবে এমন কিছু ঘটনা রয়েছে যেখানে সাবক্লাস বেস ক্লাসের কার্যকারিতা ওভাররাইড করতে পারে এবং হ্যাঁ, এটি এলএসপি লঙ্ঘন করতে পারে তবে এর মুখোমুখি হতে দেয়, IHerbivoreEaterএটি কেবল একটি সুবিধাজনক পরীক্ষামূলক ইন্টারফেস - এটি শেষ ব্যবহারকারীর পক্ষে কোনও ব্যাপার না)। সুতরাং এই পরীক্ষাগুলির কিছু মূল্য আছে বলে আমি মনে করি।

এই পরিস্থিতিতে অন্যান্য লোকেরা কী করবে? আপনি কি কেবল নিজের পরীক্ষাটি বেস ক্লাসে সরিয়ে নিয়েছেন, বা প্রত্যাশিত আচরণের জন্য আপনি সমস্ত সাবক্লাসটি পরীক্ষা করেন?

সম্পাদনা :

@ পিডিআর থেকে প্রাপ্ত উত্তরের ভিত্তিতে আমি মনে করি আমাদের এটি বিবেচনা করা উচিত: এটি IHerbivoreEaterকেবল একটি পদ্ধতি স্বাক্ষরের চুক্তি; এটি আচরণ নির্দিষ্ট করে না। এই ক্ষেত্রে:

[TestMethod]
public void Tiger_eats_herbivore_haunches_first()
{
    IHerbivoreEater_eats_herbivore_haunches_first(BuildTigerForTest);
}

[TestMethod]
public void Cheetah_eats_herbivore_haunches_first()
{
    IHerbivoreEater_eats_herbivore_haunches_first(BuildCheetahForTest);
}

[TestMethod]
public void Lion_eats_herbivore_head_first()
{
    IHerbivoreEater_eats_herbivore_head_first(BuildLionForTest);
}

তর্কের খাতিরে, আপনার কি এমন একটি Animalক্লাস থাকা উচিত নয় যা রয়েছে Eat? সমস্ত প্রাণী খায়, এবং সুতরাং Tigerএবং Lionশ্রেণি প্রাণী থেকে উত্তরাধিকারী হতে পারে।
দি মফিন ম্যান

1
@ নিক - এটি একটি ভাল বিষয়, তবে আমি মনে করি এটি একটি ভিন্ন পরিস্থিতি। @ পিডিআর হিসাবে উল্লেখ করা হয়েছে, আপনি যদি এই Eatআচরণটি বেস শ্রেণিতে রাখেন তবে সমস্ত সাবক্লাসের একই Eatআচরণ প্রদর্শন করা উচিত । যাইহোক, আমি 2 অপেক্ষাকৃত অপ্রাসঙ্গিক শ্রেণীর কথা বলছি যা কোনও আচরণ ভাগ করে নেওয়ার জন্য ঘটে। উদাহরণস্বরূপ, আমরা বিবেচনা করতে পারি এবং এর Flyআচরণগুলি বিবেচনা করুন, আমরা একই ধরণের উড়ন্ত আচরণ প্রদর্শন করতে পারি তবে এগুলি একটি সাধারণ বেস শ্রেণি থেকে প্রাপ্ত হওয়া অগত্যা বোধগম্য নয়। BrickPerson
স্কট হুইটলক

উত্তর:


6

এটি দুর্দান্ত কারণ এটি দেখায় যে কীভাবে পরীক্ষাগুলি আপনার নকশা সম্পর্কে চিন্তাভাবনা করে। আপনি ডিজাইনে সমস্যাগুলি সংবেদন করছেন এবং সঠিক প্রশ্ন জিজ্ঞাসা করছেন।

এটি দেখার দুটি উপায় আছে।

IHerbivoreEater একটি চুক্তি। সমস্ত IHerbivoreEters এর অবশ্যই একটি Eat পদ্ধতি থাকতে হবে যা একটি হার্বিবোর গ্রহণ করে। এখন, আপনার পরীক্ষাগুলি কীভাবে এটি খাওয়া হয় তা যত্ন করে না; আপনার সিংহটি পাগলের সাথে শুরু হতে পারে এবং বাঘটি গলায় শুরু হতে পারে। আপনার সমস্ত পরীক্ষার জন্য যত্নশীল তা হ'ল এটি খাওয়ার পরে, হার্বিবোর খাওয়া হয়।

অন্যদিকে, আপনি যা বলছেন তার একটি অংশ হ'ল সমস্ত IHerbivoreEters হের্বিভোরকে ঠিক একইভাবে খায় (অতএব বেস শ্রেণি)। এটি হ'ল, আইএইচরবিভোরএটার চুক্তিটি মোটেও লাভ করার কোনও মানে নেই। এটি কিছুই সরবরাহ করে না। আপনি কেবল হার্বিবোরএটার থেকে উত্তরাধিকারী হতে পারেন।

অথবা সিংহ এবং টাইগারকে পুরোপুরি সরিয়ে ফেলুন।

তবে, সিংহ এবং টাইগার যদি খাওয়ার অভ্যাস বাদে প্রতিটি অর্থে পৃথক হয়, তবে আপনাকে ভাবতে শুরু করা উচিত যে আপনি কোনও উত্তরাধিকারসূত্রে গাছ নিয়ে সমস্যায় পড়তে চলেছেন কি না। আপনি যদি উভয় ক্লাসই ফিলাইন থেকে, বা কিংঅফআইটসডোমাইন (কেবল শার্ক সহ, সম্ভবত) থেকে লায়ন ক্লাস দুটি পেতে চান তবে কী হবে। এইখানেই এলএসপি সত্যই আসে।

আমি পরামর্শ দেব যে প্রচলিত কোডটি আরও ভাল এনক্যাপসুলেটেড।

public class Lion : IHerbivoreEater
{
    private IHerbivoreEatingStrategy _herbivoreEatingStrategy;
    private Lion (IHerbivoreEatingStrategy herbivoreEatingStrategy)
    {
        _herbivoreEatingStrategy = herbivoreEatingStrategy;
    }

    public Lion() : this(new StandardHerbivoreEatingStrategy())
    {
    }

    public void Eat(Herbivore herbivore)
    {
        _herbivoreEatingStrategy.Eat(herbivore);
    }
}

বাঘের জন্য একই যায়।

এখন, এখানে একটি সুন্দর জিনিস বিকাশ করছে (সুন্দর কারণ আমি এটির উদ্দেশ্য করি নি)। যদি আপনি কেবলমাত্র সেই প্রাইভেট কনস্ট্রাক্টরকে পরীক্ষার জন্য উপলব্ধ করেন তবে আপনি একটি জাল IHerbivoreEatingStrategy এ পাস করতে পারেন এবং কেবল পরীক্ষা করতে পারেন যে বার্তাটি সঠিকভাবে এনক্যাপসুলেটেড অবজেক্টে দেওয়া হয়েছে।

এবং আপনার জটিল পরীক্ষা, যার সম্পর্কে আপনি প্রথম দিকে চিন্তিত ছিলেন, কেবলমাত্র তাকে স্ট্যান্ডার্ডহিরভিওরেইটিংস্ট্রেজি পরীক্ষা করতে হবে। এক শ্রেণি, পরীক্ষার একটি সেট, উদ্বেগের জন্য কোনও সদৃশ কোড নেই।

এবং, যদি পরে, আপনি টাইগারদের বলতে চান যে তাদের নিরামিষাশীদের আলাদা উপায়ে খাওয়া উচিত, তবে এই পরীক্ষাগুলির কোনওটিই বদলাতে হবে না। আপনি কেবল একটি নতুন হার্বাইভোরএটিংস্ট্রেজি তৈরি করছেন এবং এটি পরীক্ষা করছেন। তারের সংহতকরণ-পরীক্ষার পর্যায়ে পরীক্ষা করা হয়।


+1 কৌশল প্যাটার্নটি প্রথম জিনিসটি যা আমার মাথায় এসেছিল প্রশ্নটি পড়ে।
StuperUser

খুব ভাল, তবে এখন "ইন্টিগ্রেশন টেস্ট" দিয়ে আমার প্রশ্নের "ইউনিট পরীক্ষা" প্রতিস্থাপন করুন। আমরা কি একই সমস্যাটি শেষ করব না? IHerbivoreEaterএটি একটি চুক্তি, তবে কেবল পরীক্ষার জন্য আমার এটির প্রয়োজন। আমার কাছে মনে হচ্ছে এটি এমন একটি ঘটনা যেখানে হাঁসের টাইপিং সত্যিই সাহায্য করবে। আমি এখনই তাদের উভয়কে একই পরীক্ষার যুক্তিতে প্রেরণ করতে চাই। আমি মনে করি না ইন্টারফেসটি আচরণের প্রতিশ্রুতি দেওয়া উচিত। পরীক্ষা করা উচিত।
স্কট হুইটলক

দুর্দান্ত প্রশ্ন, দুর্দান্ত উত্তর আপনার কোনও ব্যক্তিগত নির্মাণকারীর দরকার নেই; আপনি আইওসি কনটেইনার ব্যবহার করে স্ট্যান্ডার্ডহিরভিওরেইটিংস্ট্রেটিকে IHerbivoreEatingStrategy ওয়্যার করতে পারেন।
আজহেগ্লোভ

@ স্কটউইটলক: "আমি মনে করি না ইন্টারফেসটির আচরণের প্রতিশ্রুতি দেওয়া উচিত। পরীক্ষাগুলিও এটি করা উচিত।" আমি ঠিক তাই বলছি। যদি এটি আচরণের প্রতিশ্রুতি দেয় তবে আপনার এটি থেকে মুক্তি পাওয়া উচিত এবং কেবল (বেস) শ্রেণিটি ব্যবহার করা উচিত। আপনার পরীক্ষার জন্য একেবারেই দরকার নেই।
pdr

@ আজেগ্লোভ: সম্মত হন, তবে আমার উত্তরটি ইতিমধ্যে যথেষ্ট ছিল :)
পিডিআর

1

একই কোডটি একাধিকবার পরীক্ষা করা অযথা মনে হয়, তবে এমন কিছু ক্ষেত্রে রয়েছে যেখানে সাবক্লাস বেস ক্লাসের কার্যকারিতা ওভাররাইড করতে পারে এবং করতে পারে

প্রায় চারদিকে আপনি কিছু পরীক্ষা বাদ দিতে সাদা বাক্স জ্ঞান ব্যবহার করা উপযুক্ত কিনা তা জিজ্ঞাসা করছেন। একটি কালো-বাক্স দৃষ্টিকোণ থেকে, Lionএবং Tigerবিভিন্ন শ্রেণীর হয়। সুতরাং কোডটির সাথে অজানা কেউ তাদের পরীক্ষা করবে, তবে গভীর বাস্তবায়নের জ্ঞান সহ আপনি জানেন যে কেবল একটি প্রাণীর পরীক্ষা করে আপনি পালিয়ে যেতে পারেন।

ইউনিট পরীক্ষাগুলি বিকাশের কারণগুলির একটি অংশ হ'ল পরে নিজেকে রিফ্যাক্টর সরবরাহ করা কিন্তু একই ব্ল্যাক-বাক্স ইন্টারফেস বজায় রাখা । ইউনিট পরীক্ষাগুলি আপনাকে ক্লাসীদের সাথে তাদের চুক্তি পূরণ করা অব্যাহত রাখতে বা খুব কমপক্ষে আপনাকে চুক্তিটি কীভাবে পরিবর্তিত হতে পারে তা অনুধাবন করতে এবং সাবধানতার সাথে ভাবতে বাধ্য করতে সহায়তা করে। আপনি নিজেই বুঝতে পেরেছেন Lionবা পরে কোনও সময়ে Tigerওভাররাইড করতে পারেন Eat। যদি এটি দূরবর্তীভাবে সম্ভব হয়, তবে একটি সাধারণ ইউনিট পরীক্ষার পরীক্ষা যা আপনার সমর্থন করা প্রতিটি প্রাণী, সেই পদ্ধতিতে খেতে পারে:

[TestMethod]
public void Tiger_can_eat_herbivore()
{
    IHerbivoreEater_can_eat_herbivore(BuildTigerForTest);
}

করণ এবং যথেষ্ট করার পক্ষে খুব সহজ হওয়া উচিত এবং এটি নিশ্চিত করা উচিত যে কখন বস্তুগুলি তাদের চুক্তি পূরণ করতে ব্যর্থ হয় you


আমি অবাক হয়েছি যে এই প্রশ্নটি যদি সত্যিই কেবল কালো-বনাম সাদা-বাক্স পরীক্ষার একটি পছন্দের দিকে সিদ্ধ হয়। আমি ব্ল্যাক-বক্স শিবিরের দিকে ঝুঁকছি, যার কারণেই সম্ভবত আমি যেভাবে আছি তার পরীক্ষা করছি। যে ইশারা জন্য ধন্যবাদ।
স্কট হুইটলক

1

তুমি এটি ঠিক করতেছ. আপনার নতুন কোডের একক ব্যবহারের আচরণের পরীক্ষা হিসাবে একক পরীক্ষার কথা ভাবেন। আপনি একই কোডটি প্রডাকশন কোড থেকে করবেন।

এই পরিস্থিতিতে, আপনি সম্পূর্ণরূপে সঠিক; সিংহ বা বাঘের ব্যবহারকারীর (কমপক্ষে, প্রয়োজন নেই) খেয়াল রাখবেন যে তারা উভয়ই হার্বাইভোরইটার এবং হ'ল পদ্ধতিটি যে পদ্ধতিটির জন্য চালিত হয় তা বেস শ্রেণিতে উভয়েরই পক্ষে সাধারণ। একইভাবে, হার্বিবোরএটার বিমূর্ত ব্যবহারকারীর (কংক্রিট সিংহ বা টাইগার দ্বারা সরবরাহ করা) এটি যা আছে তা যত্ন করে না। তারা যে বিষয়টির বিষয়ে চিন্তা করে তা হ'ল তাদের সিংহ, বাঘ, বা হার্বিভোরএটারের অজানা কংক্রিট প্রয়োগ কার্যকরভাবে একটি হার্বিভোর খাবে ()।

সুতরাং, আপনি মূলত যা যা পরীক্ষা করছেন তা হ'ল একটি সিংহ ইচ্ছামত খাওয়া হবে এবং বাঘ যেমন ইচ্ছা মতো খাবে। এটি উভয়েরই পরীক্ষা করা গুরুত্বপূর্ণ, কারণ এটি সর্বদা সত্য নাও হতে পারে যে তারা উভয়ই ঠিক একইভাবে খায়; উভয়ই পরীক্ষা করে আপনি নিশ্চিত হন যে আপনি যেটিকে পরিবর্তন করতে চান না, তা নয়। কারণ এগুলি উভয়ই হার্বাইভোরইটারের সংজ্ঞায়িত, কমপক্ষে আপনি চিতাকে যুক্ত না করা পর্যন্ত আপনিও পরীক্ষা করেছেন যে সমস্ত হার্বাইভোরইটারগুলি ইচ্ছামত খাবেন। আপনার টেস্টিং উভয়ই সম্পূর্ণ কভার করে এবং পর্যাপ্তভাবে কোডটি অনুশীলন করে (আপনি যদি কোনও হার্বাইভোর খাওয়া হার্বিভোর খাওয়ার ফলে কী হবে তার ফলাফলের সমস্ত প্রত্যাশিত দৃ expected়তা তৈরি করেছেন)।

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