यूनिट परीक्षण अपेक्षित परिणाम हार्डकोड होना चाहिए?


29

क्या यूनिट टेस्ट के अपेक्षित परिणाम हार्डकोड किए जा सकते हैं, या क्या वे आरंभिक चर पर निर्भर हो सकते हैं? क्या हार्डकोड या गणना किए गए परिणाम यूनिट टेस्ट में त्रुटियों को पेश करने के जोखिम को बढ़ाते हैं? क्या अन्य कारक हैं जिन पर मैंने विचार नहीं किया है?

उदाहरण के लिए, इन दोनों में से कौन अधिक विश्वसनीय प्रारूप है?

[TestMethod]
public void GetPath_Hardcoded()
{
    MyClass target = new MyClass("fields", "that later", "determine", "a folder");
    string expected = "C:\\Output Folder\\fields\\that later\\determine\\a folder";
    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

[TestMethod]
public void GetPath_Softcoded()
{
    MyClass target = new MyClass("fields", "that later", "determine", "a folder");
    string expected = "C:\\Output Folder\\" + string.Join("\\", target.Field1, target.Field2, target.Field3, target.Field4);
    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

EDIT 1: DXM के उत्तर के जवाब में, विकल्प 3 एक पसंदीदा समाधान है?

[TestMethod]
public void GetPath_Option3()
{
    string field1 = "fields";
    string field2 = "that later";
    string field3 = "determine";
    string field4 = "a folder";
    MyClass target = new MyClass(field1, field2, field3, field4);
    string expected = "C:\\Output Folder\\" + string.Join("\\", field1, field2, field3, field4);
    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

2
दोनों करो। गंभीरता से। टेस्ट कर सकते हैं और ओवरलैप करना चाहिए। यदि आप स्वयं को हार्ड-कोडित मूल्यों के साथ काम करते हुए पाते हैं, तो कुछ प्रकार के डेटा-चालित परीक्षण देखें।
जॉब

मैं तीसरे विकल्प पर सहमत होता हूं जो मुझे उपयोग करना पसंद है। मुझे नहीं लगता कि आप संकलन पर हेरफेर को खत्म करने के बाद विकल्प 1 को नुकसान पहुंचाएंगे।
kwelch

आपके दोनों विकल्प हार्डकोडिंग का उपयोग करते हैं और यदि C: \\
Qwertie

जवाबों:


27

मुझे लगता है कि अधिक मजबूत और लचीले परीक्षण मामलों में अपेक्षित मूल्य परिणाम की गणना की गई है। अपेक्षित परिणाम की गणना करने वाले अभिव्यक्ति में अच्छे चर नामों का उपयोग करके, यह बहुत अधिक स्पष्ट है कि अपेक्षित परिणाम पहले स्थान पर कहां से आया है।

ऐसा कहने के बाद, आपके विशिष्ट उदाहरण में मुझे "सॉफ्टकोड" पद्धति पर भरोसा नहीं होगा क्योंकि यह आपकी गणना के लिए इनपुट के रूप में आपके SUT (परीक्षण के तहत प्रणाली) का उपयोग करता है। यदि MyClass में एक बग है जहाँ फ़ील्ड ठीक से संग्रहीत नहीं हैं, तो आपका परीक्षण वास्तव में पास होगा क्योंकि आपकी अपेक्षित मान गणना लक्ष्य की तरह गलत स्ट्रिंग का उपयोग करेगी। गेटपैथ ()।

मेरा सुझाव अपेक्षित मूल्य की गणना करना होगा जहां यह समझ में आता है, लेकिन यह सुनिश्चित करें कि गणना SUT से किसी भी कोड पर निर्भर नहीं करती है।

मेरी प्रतिक्रिया में ओपी के अपडेट के जवाब में:

हां, मेरे ज्ञान के आधार पर लेकिन TDD करने में कुछ हद तक सीमित अनुभव है, मैं विकल्प # 3 चुनूंगा।


1
अच्छी बात! परीक्षण में असत्यापित वस्तु पर भरोसा मत करो।
हाथ-ई-खाद्य

क्या यह SUT कोड का दोहराव नहीं है?
अब्येक

1
एक तरह से यह है, लेकिन यह है कि आप कैसे सत्यापित करें कि SUT काम कर रहा है। यदि हम एक ही कोड का उपयोग करते हैं और इसका भंडाफोड़ हो जाता है, तो आप कभी नहीं जान पाएंगे। बेशक, यदि गणना करने के लिए, आपको SUT की बहुत नकल करने की आवश्यकता है, तो शायद विकल्प # 1 बेहतर हो जाएगा, बस मूल्य को हार्डकोड करें।
DXM

16

क्या होगा यदि कोड इस प्रकार था:

MyTarget() // constructor
{
   Field1 = Field2 = Field3 = Field4 = "";
}

आपका दूसरा उदाहरण बग को नहीं पकड़ेगा, लेकिन पहला उदाहरण होगा।

सामान्य तौर पर, मैं सॉफ्ट-कोडिंग के खिलाफ सुझाव दूंगा क्योंकि यह बग को छिपा सकता है। उदाहरण के लिए:

string expected = "C:\\Output Folder" + string.Join("\\", target.Field1, target.Field2, target.Field3, target.Field4);

क्या आप समस्या को देख सकते हैं? आप हार्ड-कोडेड संस्करण में वही गलती नहीं करेंगे। हार्ड-कोडित मूल्यों की तुलना में गणनाओं को सही करना कठिन है। इसलिए मैं सॉफ्ट-कोडेड की तुलना में हार्ड-कोडेड मूल्यों के साथ काम करना पसंद करता हूं।

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


मैं सुनता हूं कि आप क्या कह रहे हैं और इससे मुझे विचार करने का अवसर मिलता है। सॉफ्टकोडिंग मेरे अन्य परीक्षण मामलों पर निर्भर करता है (जैसे कंस्ट्रक्टरशोल्डकोरेरीली इंसुलेटिज़ फील्ड्स) गुजर रहा है। आपके द्वारा बताई गई असफलता अन्य इकाई परीक्षणों द्वारा विफल हो जाएगी।
हैंड-ई-फूड

@ हाथ-ई-खाद्य, ऐसा लगता है जैसे आप अपनी वस्तुओं के व्यक्तिगत तरीकों पर परीक्षण लिख रहे हैं। मत करो। आपको ऐसे परीक्षण लिखने चाहिए जो आपके संपूर्ण ऑब्जेक्ट की शुद्धता की जांच करें न कि अलग-अलग तरीकों से। अन्यथा आपके परीक्षण वस्तु के अंदर परिवर्तन के संबंध में भंगुर होंगे।
विंस्टन इर्वर्ट

मुझे यकीन नहीं है कि मैं फॉलो करता हूं। मैंने जो उदाहरण दिया वह विशुद्ध रूप से काल्पनिक था, परिदृश्य को समझने में आसान। मैं कक्षाओं और वस्तुओं के सार्वजनिक सदस्यों का परीक्षण करने के लिए इकाई परीक्षण लिख रहा हूं। क्या उनका उपयोग करने का सही तरीका है?
हाथ-ई-खाद्य

@ हाथ-ई-फूड, अगर मैं आपको सही तरीके से समझता हूं, तो आपका परीक्षण ConstructShouldCorrectlyInitialiseFields निर्माणकर्ता को आमंत्रित करेगा और फिर पुष्टि करेगा कि फ़ील्ड सही तरीके से सेट हैं। लेकिन आपको ऐसा नहीं करना चाहिए। आपको ध्यान नहीं देना चाहिए कि आंतरिक क्षेत्र क्या कर रहे हैं। आपको केवल यह कहना चाहिए कि वस्तु का बाहरी व्यवहार सही है। अन्यथा, वह दिन आ सकता है जब आपको आंतरिक कार्यान्वयन को प्रतिस्थापित करने की आवश्यकता होती है। यदि आपने आंतरिक स्थिति के बारे में जोर दिया है, तो आपके सभी परीक्षण टूट जाएंगे। लेकिन अगर आपने केवल बाहरी व्यवहार के बारे में जोर दिया है, तब भी सब कुछ काम करेगा।
विंस्टन इर्वर्ट

@ विंस्टन - मैं वास्तव में xUnit टेस्ट पैटर्न बुक के माध्यम से जुताई करने की प्रक्रिया में हूं और इससे पहले द आर्ट ऑफ़ यूनिट टेस्टिंग समाप्त हो गया। मैं बहाना करने वाला नहीं हूं मुझे पता है कि मैं किस बारे में बात कर रहा हूं, लेकिन मुझे लगता है कि मैं उन किताबों से कुछ लेना चाहता हूं। दोनों पुस्तकें दृढ़ता से अनुशंसा करती हैं कि प्रत्येक परीक्षण पद्धति को पूर्ण न्यूनतम परीक्षण करना चाहिए और आपके पास अपनी संपूर्ण वस्तु का परीक्षण करने के लिए कई परीक्षण मामले होने चाहिए। इस तरह जब इंटरफेस या कार्यक्षमता बदलती है, तो आपको उनमें से अधिकांश के बजाय केवल कुछ परीक्षण विधियों को ठीक करने की उम्मीद करनी चाहिए। और चूंकि वे छोटे हैं, इसलिए परिवर्तन आसान होना चाहिए।
डेसीएम

4

मेरी राय में, आपके दोनों सुझाव आदर्श से कम हैं। इसे करने का आदर्श तरीका यह है:

[TestMethod]
public void GetPath_Hardcoded()
{
    const string f1 = "fields"; const string f2 = "that later"; 
    const string f3 = "determine"; const string f4 = "a folder";

    MyClass target = new MyClass( f1, f2, f3, f4 );
    string expected = "C:\\Output Folder\\" + string.Join("\\", f1, f2, f3, f4);
    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

दूसरे शब्दों में, परीक्षण को विशेष रूप से इनपुट और ऑब्जेक्ट के आउटपुट के आधार पर काम करना चाहिए, न कि ऑब्जेक्ट की आंतरिक स्थिति के आधार पर। वस्तु को ब्लैक बॉक्स के रूप में माना जाना चाहिए। (मैं अन्य मुद्दों की अवहेलना करता हूं, जैसे स्ट्रिंग का उपयोग करने की अनुपयुक्तता। पथ के बजाय कनेक्ट करें। कोम्बाइन, क्योंकि यह सिर्फ एक उदाहरण है।)


1
सभी विधियां कार्यात्मक नहीं हैं - कई सही ढंग से दुष्प्रभाव होते हैं जो किसी वस्तु या वस्तुओं की स्थिति को बदलते हैं। साइड इफेक्ट के साथ एक विधि के लिए एक इकाई परीक्षण संभवतः विधि से प्रभावित वस्तु (ओं) की स्थिति का मूल्यांकन करने की आवश्यकता होगी।
मैथ्यू फ्लिन

तब उस राज्य को विधि के उत्पादन के रूप में माना जाएगा। इस नमूना परीक्षण का आशय गेटपाथ () विधि की जाँच करना है, न कि माईक्लास के निर्माणकर्ता का। @ डीएक्सएम का जवाब पढ़ें, वह ब्लैक बॉक्स दृष्टिकोण लेने के लिए एक बहुत अच्छा कारण प्रदान करता है।
माइक नाकिस

@MatthewFlynn, तो आपको उस स्थिति से प्रभावित विधियों का परीक्षण करना चाहिए। सटीक आंतरिक स्थिति एक कार्यान्वयन विवरण है और परीक्षण के व्यवसाय में से कोई भी नहीं।
विंस्टन एवर्ट

@MatthewFlynn, केवल स्पष्ट करने के लिए, क्या वह उदाहरण दिखाए गए उदाहरण से संबंधित है, या अन्य इकाई परीक्षणों पर विचार करने के लिए कुछ और है? मैं उस बात को कुछ इस तरह से देख सकता था target.Dispose(); Assert.IsTrue(target.IsDisposed);(एक बहुत ही सरल उदाहरण।)
हाथ-ई-फूड

इस मामले में भी, IsDisposed संपत्ति वर्ग के सार्वजनिक इंटरफ़ेस का एक अनिवार्य हिस्सा है (या होना चाहिए), और कार्यान्वयन विवरण नहीं है। (IDispose इंटरफ़ेस ऐसी कोई संपत्ति प्रदान नहीं करता है, लेकिन यह दुर्भाग्यपूर्ण है।)
माइक नाकिस

2

चर्चा में दो पहलू हैं:

1. परीक्षण मामले के लिए स्वयं लक्ष्य का उपयोग करना
। पहला प्रश्न यह होना चाहिए कि क्या आप परीक्षण स्टब में किए गए कार्य का हिस्सा होने और भरोसा करने के लिए स्वयं कक्षा का उपयोग कर सकते हैं ? - जवाब नहीं के बाद से है, सामान्य तौर पर, आपको कभी भी उस कोड के बारे में धारणा नहीं बनानी चाहिए जिसका आप परीक्षण कर रहे हैं। यदि यह ठीक से नहीं किया जाता है, तो समय के साथ कीड़े कुछ इकाई परीक्षण के लिए प्रतिरक्षा बन जाते हैं।

2. हार्डकोडिंग के लिए
आपको हार्ड कोड चाहिए ? फिर से जवाब है नहीं । क्योंकि किसी भी सॉफ्टवेयर की तरह - जब चीजें विकसित होती हैं, तो सूचनाओं की हार्ड कोडिंग मुश्किल हो जाती है। उदाहरण के लिए, जब आप उपरोक्त पथ को फिर से संशोधित करना चाहते हैं, तो आपको अतिरिक्त इकाई लिखने या संशोधित करने की आवश्यकता होगी। एक बेहतर तरीका यह है कि इनपुट और मूल्यांकन तिथि को अलग-अलग कॉन्फ़िगरेशन से प्राप्त किया जाए जिसे आसानी से अनुकूलित किया जा सके।

उदाहरण के लिए यहाँ है कि मैं परीक्षण ठूंठ को कैसे ठीक करूँगा।

[TestMethod]
public void GetPath_Tested(int CaseId)
{
    testParams = GetTestConfig(caseID,"testConfig.txt"); // some wrapper that does read line and chops the field. 
    MyClass target = new MyClass(testParams.field1, testParams.field2);
    string expected = testParams.field5;
    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

0

बहुत सी अवधारणाएँ संभव हैं, अंतर को देखने के लिए कुछ उदाहरण बनाए

[TestMethod]
public void GetPath_Softcoded()
{
    //Hardcoded since you want to see what you expect is most simple and clear
    string expected = "C:\\Output Folder\\fields\\that later\\determine\\a folder";

    //If this test should also use a mocked filesystem it might be that you want to use
    //some base directory, which you could set in the setUp of your test class
    //that is usefull if you you need to run the same test on different environments
    string expected = this.outputPath + "fields\\that later\\determine\\a folder";


    //another readable way could be interesting if you have difficult variables needed to test
    string fields = "fields";
    string thatLater = "that later";
    string determine = "determine";
    string aFolder = "a folder";
    string expected = this.outputPath + fields + "\\" + thatLater + "\\" + determine + "\\" + aFolder;
    MyClass target = new MyClass(fields, thatLater, determine, aFolder);

    //in general testing with real words is not needed, so code could be shorter on that
    //for testing difficult folder names you write a separate test anyway
    string f1 = "f1";
    string f2 = "f2";
    string f3 = "f3";
    string f4 = "f4";
    string expected = this.outputPath + f1 + "\\" + f2 + "\\" + f3 + "\\" + f4;
    MyClass target = new MyClass(f1, f2, f3, f4);

    //so here we start to see a structure, it looks more like an array of fields
    //so what would make testing more interesting with lots of variables is the use of a data provider
    //the data provider will re-use your test with many different kinds of inputs. That will reduce the amount of duplication of code for testing
    //http://msdn.microsoft.com/en-us/library/ms182527.aspx


    The part where you compare already seems correct
    MyClass target = new MyClass(fields, thatLater, determine, aFolder);

    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

संक्षेप में: सामान्य तौर पर आपका पहला जस्ट हार्डकोडेड टेस्ट मेरे लिए सबसे ज्यादा मायने रखता है क्योंकि यह सरल है, सीधे बिंदु पर है आदि। यदि आप हार्डकॉडिंग शुरू करते हैं तो कई बार बस सेटअप विधि में डालते हैं।

भविष्य के संरचित परीक्षण के लिए मैं डेटा स्रोतों की जांच करने जाऊंगा ताकि आप अधिक डेटा पंक्तियों को जोड़ सकें यदि आपको अधिक परीक्षण स्थितियों की आवश्यकता है।


0

आधुनिक परीक्षण रूपरेखा आपको अपनी पद्धति के लिए पैरामीटर प्रदान करने की अनुमति देती है। मैं उन लोगों का लाभ उठाऊंगा:

[TestCase("fields", "that later", "determine", "a folder", @"C:\Output Folder\fields\that later\determine\a folder")]
public void GetPathShouldReturnFullDirectoryPathBasedOnItsFields(
    string field1, string field2, string field3, string field,
    string expected)
{
    MyClass target = new MyClass(field1, field2, field3, field4);
    string actual = target.GetPath();
    Assert.AreEqual(expected, actual,
        "GetPath should return a full directory path based on its fields.");
}

मेरे विचार में इसके कई फायदे हैं:

  1. डेवलपर्स को अक्सर SUT से उनके यूनिट परीक्षणों में कोड के प्रतीत होने वाले सरल भागों की नकल करने के लिए लुभाया जाता है। जैसा कि विंस्टन बताते हैं , उन में अभी भी मुश्किल कीड़े छिपे हो सकते हैं। "हार्ड-कोडिंग" अपेक्षित परिणाम उन परिस्थितियों से बचने में मदद करता है जहां आपका परीक्षण कोड उसी कारण से गलत है, जहां आपका मूल कोड गलत है। लेकिन अगर आवश्यकताओं में बदलाव आपको दर्जनों परीक्षण विधियों के अंदर एम्बेडेड हार्ड-कोडिंग स्ट्रिंग्स को ट्रैक करने के लिए मजबूर करता है, तो यह कष्टप्रद हो सकता है। आपके परीक्षण तर्क के बाहर, सभी हार्ड-कोडित मानों को एक स्थान पर रखने से, आपको दोनों दुनियाओं का सर्वश्रेष्ठ अनुभव प्राप्त होता है।
  2. आप कोड की एक पंक्ति के साथ विभिन्न इनपुट और अपेक्षित आउटपुट के लिए परीक्षण जोड़ सकते हैं। यह आपको अधिक परीक्षण लिखने के लिए प्रोत्साहित करता है, जबकि आपके परीक्षण कोड DRY और बनाए रखने में आसान है। मुझे लगता है कि क्योंकि परीक्षण जोड़ने के लिए यह बहुत सस्ता है, मेरा मन नए परीक्षण मामलों के लिए खुला है, जिनके बारे में मैंने सोचा नहीं था कि मुझे उनके लिए एक पूरी नई विधि लिखनी है। उदाहरण के लिए, अगर किसी एक इनपुट में डॉट था तो मैं किस व्यवहार की उम्मीद करूंगा? एक बैकस्लैश? अगर कोई खाली था तो क्या होगा? या व्हॉट्सएप? या व्हाट्सएप से शुरू किया या खत्म किया?
  3. परीक्षण रूपरेखा प्रत्येक TestCase को अपना परीक्षण मानती है, यहां तक ​​कि प्रदान किए गए इनपुट और आउटपुट को भी परीक्षण नाम में डालती है। यदि सभी TestCases पास हो जाते हैं, लेकिन एक, यह देखना आसान है कि कौन सा टूट गया और यह सभी दूसरों से अलग कैसे था।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.