वर्गों की एक श्रेणी है जो परीक्षण करना बहुत आसान है। यदि एक वर्ग केवल आदिम डेटा प्रकारों पर निर्भर करता है और अन्य व्यावसायिक संस्थाओं के साथ कोई संबंध नहीं है, तो बस इस वर्ग का एक उदाहरण बनाएं, किसी संपत्ति को बदलने या किसी विधि को कॉल करके और अपेक्षित स्थिति की जांच करके किसी तरह से इसे "किक" करें।
यह परीक्षण का सबसे सरल और सबसे प्रभावी तरीका है, और कोई भी समझदार डिजाइन ऐसी कक्षाओं पर आधारित है, जो निचले स्तर के "बिल्डिंग ब्लॉक" हैं, जिसके आधार पर फिर अधिक जटिल सार बनाया जाता है। लेकिन ऐसे "अलगाव" में रहने वाले वर्गों की संख्या प्रकृति में ज्यादा नहीं है। यहां तक कि अगर हम सामान्य रूप से डेटाबेस (या सेवा) के साथ एक अलग वर्ग (या कक्षाओं के सेट) में काम करने के लिए सभी तर्क को सामान्य करते हैं, तो जल्द ही या बाद में कोई दिखाई देगा जो इन कक्षाओं का उपयोग उच्च-स्तरीय व्यवहार प्राप्त करने के लिए करेगा और यह "कोई" फिर "भी परीक्षण करने की आवश्यकता होगी।
लेकिन पहले, आइए एक अधिक विशिष्ट मामले को देखें जब डेटाबेस या बाहरी सेवा के साथ काम करने के लिए तर्क, साथ ही इस डेटा को संसाधित करने के लिए तर्क एक ही स्थान पर केंद्रित है।
// , // public class LoginViewModel { public LoginViewModel() { // UserName = ReadLastUserName(); } // ; public string UserName { get; set; } // UserName public void Login() { // , // . // SaveLastUserName(UserName); } // private string ReadLastUserName() { // , ... // -, return "Jonh Doe"; } // private void SaveLastUserName(string lastUserName) { // , , } }
जब ऐसी कक्षाओं का परीक्षण करने की बात आती है, तो यह दृश्य मॉडल आमतौर पर फ़ॉर्म पर रखा जाता है, जिसे बाद में हाथों से परीक्षण किया जाता है। यदि दृश्य मॉडल के बजाय सर्वर घटकों को लागू करते समय ऐसा लॉजिक मिक्सिंग होता है, तो उन्हें एक साधारण कंसोल एप्लिकेशन बनाकर परीक्षण किया जाता है जो आवश्यक उच्च-स्तर को लागू करेगा। फ़ंक्शन, इस प्रकार पूरे मॉड्यूल का परीक्षण कर रहा है। दोनों ही मामलों में, इस परीक्षण विकल्प को बहुत स्वचालित नहीं कहा जा सकता है।
नोट
मुझे चिल्लाते हुए पत्थर फेंकने की आवश्यकता नहीं है: "आज कौन बकवास लिख सकता है?" आखिरकार, इस तरह के दृष्टिकोण के खतरों के बारे में पहले से ही बहुत कुछ लिखा गया है, और वास्तव में, हमारे पास एकता shuniti और अन्य उपयोगिताओं हैं, इसलिए यह बीस साल पहले एक अवास्तविक समझौता है! " वैसे, हाँ, यह एक बटन समझौते है, लेकिन, सबसे पहले, यह इकाइयों और अन्य कंटेनरों के बारे में नहीं है, लेकिन बुनियादी सिद्धांतों के बारे में है, और दूसरी बात, इस तरह के "एकीकरण" परीक्षण अभी भी अविश्वसनीय रूप से लोकप्रिय है, किसी भी मामले में, मेरे कई "विदेशी" लोगों में से सहयोगियों।अनुप्रयोग परीक्षण के लिए सीम बनाना
यहां तक कि अगर आप यह नहीं सोचते हैं कि हमारे विचार मॉडल का उल्लंघन कितने नए-नए डिजाइन सिद्धांतों का उल्लंघन करता है, तो यह स्पष्ट है कि इसका डिज़ाइन कुछ हद तक ... मनहूस है। वास्तव में, भले ही आप पुराने
दादा के बुकहेव पद्धति
के साथ डिजाइन करते हैं, यह स्पष्ट हो जाता है कि अंतिम उपयोगकर्ता के नाम को संरक्षित करने के सभी कार्य, डेटाबेस के साथ काम करने का तर्क (या अन्य बाहरी डेटा स्रोत) को दूर से देखने और किसी के लिए इसे "समस्या" बनाने की आवश्यकता है दूसरा और उच्च स्तर के व्यवहार को प्राप्त करने के लिए "बिल्डिंग ब्लॉक" के रूप में इस "किसी" का उपयोग करें:
internal class LastUsernameProvider { // public string ReadLastUserName() { return "Jonh Doe"; } // , public void SaveLastUserName(string userName) { } } public class LoginViewModel { // private readonly LastUsernameProvider _provider = new LastUsernameProvider(); public LoginViewModel() { // UserName = _provider.ReadLastUserName(); } public string UserName { get; set; } public void Login() { // // _provider.SaveLastUserName(UserName); } }
अभी के लिए, एक इकाई परीक्षण लिखना अभी भी मुश्किल बना हुआ है, लेकिन यह स्पष्ट हो जाता है कि
लास्ट यूएरनामप्रोविडर वर्ग के वास्तविक कार्यान्वयन को "नकली" करना कितना सरल है और हमें जिस व्यवहार की आवश्यकता है उसका अनुकरण करें। यह एक अलग इंटरफ़ेस में इस वर्ग के तरीकों का चयन करने के लिए पर्याप्त है या सिर्फ उन्हें आभासी बना देता है और वारिस में ओवरराइड करता है। उसके बाद, यह केवल हमारे दृश्य मॉडल में "ऑब्जेक्ट" को "पेंच" करने के लिए बनी हुई है।
नोट
ईमानदारी से, मैं कोड की "परीक्षणशीलता" की खातिर डिजाइन परिवर्तनों का एक बड़ा प्रशंसक नहीं हूं। जैसा कि अभ्यास से पता चलता है, एक सामान्य OO डिजाइन या तो पहले से ही पर्याप्त रूप से "परीक्षण" है या इसे बनाने के लिए केवल न्यूनतम शरीर आंदोलनों की आवश्यकता है। इस विषय पर कुछ अतिरिक्त विचार "आदर्श वास्तुकला" लेख में पाए जा सकते हैं ।"इंजेक्शन" निर्भरता के लिए किसी भी तीसरे पक्ष के पुस्तकालयों का सहारा लिए बिना भी, हम कुछ सरल तरीकों से अपने दम पर ऐसा कर सकते हैं। वांछित निर्भरता को एक अतिरिक्त कंस्ट्रक्टर के माध्यम से, एक संपत्ति के माध्यम से पारित किया जा सकता है, या एक कारखाना विधि बना सकता है जो
ILastUsernmameProvider इंटरफ़ेस
लौटाएगा ।
आइए एक निर्माता के साथ एक विकल्प देखें, जो काफी सरल और लोकप्रिय है (बाहरी निर्भरता की एक छोटी संख्या के साथ, यह ठीक काम करता है)।
// internal interface ILastUsernameProvider { string ReadLastUserName(); void SaveLastUserName(string userName); } internal class LastUsernameProvider : ILastUsernameProvider { // public string ReadLastUserName() { return "Jonh Doe"; } // , public void SaveLastUserName(string userName) { } } public class LoginViewModel { private readonly ILastUsernameProvider _provider; // public LoginViewModel() : this(new LastUsernameProvider()) {} // "" "" internal LoginViewModel(ILastUsernameProvider provider) { _provider = provider; UserName = _provider.ReadLastUserName(); } public string UserName { get; set; } public void Login() { _provider.SaveLastUserName(UserName); } }
चूंकि अतिरिक्त निर्माणकर्ता आंतरिक है, यह केवल इस विधानसभा के अंदर ही उपलब्ध है, साथ ही यूनिट परीक्षणों के "अनुकूल" विधानसभा में भी। बेशक, अगर परीक्षण की जा रही कक्षाएं आंतरिक हैं, तो यह नहीं होगा, लेकिन चूंकि आंतरिक वर्ग के सभी "ग्राहक" एक ही विधानसभा में हैं, इसलिए उन्हें नियंत्रित करना आसान है। यह दृष्टिकोण, "नकली" व्यवहार को स्थापित करने के लिए एक आंतरिक विधि के अतिरिक्त पर आधारित है, और अधिक जटिल निर्भरता प्रबंधन तंत्र, जैसे कि IoC कंटेनरों के उपयोग पर प्रतिबंध लगाए बिना, कोड परीक्षण को सरल बनाने के लिए एक उचित समझौता है।
नोट
इंटरफेस के साथ काम करते समय कमियों में से एक पठनीयता में गिरावट है, क्योंकि यह स्पष्ट नहीं है कि इंटरफ़ेस के कितने कार्यान्वयन मौजूद हैं और जहां एक विशेष इंटरफ़ेस विधि का कार्यान्वयन स्थित है। Resharper जैसे उपकरण इस समस्या को काफी कम करते हैं क्योंकि वे न केवल विधि घोषणा (नेविगेशन के लिए घोषणा) का समर्थन करते हैं, बल्कि विधि के कार्यान्वयन के लिए नेविगेशन भी करते हैं (कार्यान्वयन पर जाएं):

स्थिति जाँच बनाम व्यवहार जाँच
अब हम
LoginViewModel वर्ग के निर्माता के लिए पहले एक इकाई परीक्षण लिखने की कोशिश करते हैं, जो अंतिम लॉग-इन उपयोगकर्ता का नाम प्राप्त करता है, और फिर
लॉगिन विधि के लिए एक इकाई परीक्षण, जिसके बाद अंतिम उपयोगकर्ता का नाम सहेजा जाना चाहिए।
इन परीक्षणों के सामान्य कार्यान्वयन के लिए, हमें इंटरफ़ेस के "नकली" कार्यान्वयन की आवश्यकता है, पहले मामले में, हमें
ReadLastUserName विधि में अंतिम उपयोगकर्ता का मनमाना नाम वापस करने की आवश्यकता है, और दूसरे मामले में, सुनिश्चित करें कि
SaveLastUserName विधि
कहा जाता है ।
यह इस प्रकार है कि दो प्रकार के "नकली" वर्ग
अलग-अलग होते हैं :
स्टब्स को परीक्षण किए गए ऑब्जेक्ट की वांछित स्थिति प्राप्त करने के लिए डिज़ाइन किया गया है , और
मोका का उपयोग परीक्षण किए गए ऑब्जेक्ट के अपेक्षित व्यवहार की जांच करने के लिए किया जाता है ।
स्टब्स का उपयोग कभी भी बयानों में नहीं किया जाता है, वे सरल "नौकर" होते हैं जो केवल टेस्ट क्लास के बाहरी वातावरण का मॉडल बनाते हैं; उसी समय, परीक्षण वर्ग की स्थिति को बयानों में सत्यापित किया जाता है, जो स्टब की स्थापित स्थिति पर निर्भर करता है।
// internal class LastUsernameProviderStub : ILastUsernameProvider { // , // public string UserName; // - UserName public string ReadLastUserName() { return UserName; } // public void SaveLastUserName(string userName) { } } [TestFixture] public class LoginViewModelTests { // - [Test] public void TestViewModelConstructor() { var stub = new LastUsernameProviderStub(); // "" stub.UserName = "Jon Skeet"; // -!! var vm = new LoginViewModel(stub); // Assert.That(vm.UserName, Is.EqualTo(stub.UserName)); } }
मोक्स की एक अलग भूमिका है। परीक्षण की गई वस्तु को "स्लिप" करें, लेकिन आवश्यक वातावरण बनाने के लिए नहीं (हालांकि वे इस भूमिका को पूरा कर सकते हैं), लेकिन सबसे पहले, ताकि बाद में सत्यापित किया जा सके कि परीक्षण
किए गए ऑब्जेक्ट
ने आवश्यक क्रियाएं की हैं । (इसीलिए इस प्रकार के परीक्षण को
राज्य - आधारित परीक्षण के लिए उपयोग किए जाने वाले स्टब्स के विपरीत
व्यवहार परीक्षण कहा जाता
है )।
// , SaveLastUserName // internal class LastUsernameProviderMock : ILastUsernameProvider { // public string SavedUserName; // , // "" "" public string ReadLastUserName() { return "Jonh Skeet";} // SavedUserName public void SaveLastUserName(string userName) { SavedUserName = userName; } } // , Login [Test] public void TestLogin() { var mock = new LastUsernameProviderMock(); var vm = new LoginViewModel(mock); // - vm.UserName = "Bob Martin"; // Login vm.Login(); // , SaveLastUserName Assert.That(mock.SavedUserName, Is.EqualTo(vm.UserName)); }
मुझे इन अंतरों के बारे में जानने की आवश्यकता क्यों है?
वास्तव में, अवधारणाओं में अंतर महत्वहीन लग सकता है, खासकर यदि आप अपने हाथों से इस तरह के "नकली" को लागू करते हैं। इस मामले में, इन पैटर्नों का ज्ञान आपको अन्य डेवलपर्स के साथ एक ही भाषा बोलने और नकली कक्षाओं के नाम को सरल बनाने की अनुमति देगा।
हालाँकि, जल्दी या बाद में आप इंटरफेस के मैनुअल कार्यान्वयन के इस अद्भुत पाठ से थक सकते हैं और आप राइनो मोक्स, Moq या Microsoft मोल्स जैसे आइसोलेशन फ्रेमवर्क में से एक पर ध्यान देंगे। वहां, इन शर्तों को आवश्यक रूप से पूरा किया जाएगा और इन प्रकारों के बीच के अंतर को समझना आपके लिए बहुत उपयोगी होगा।
मैंने जानबूझकर इनमें से किसी भी ढांचे को नहीं छुआ, क्योंकि उनमें से प्रत्येक एक अलग लेख के हकदार हैं और आईएमओ केवल इन अवधारणाओं की समझ को जटिल करेगा। लेकिन अगर आप अभी भी इन रूपरेखाओं में से कुछ को और अधिक विस्तार से देखने में रुचि रखते हैं, तो मैंने उनमें से एक के बारे में अधिक विस्तार से लिखा है:
"माइक्रोसॉफ्ट मोल्स" ।