कैसे डेटाबेस प्रश्नों के साथ एक वस्तु का परीक्षण करने के लिए यूनिट


153

मैंने सुना है कि यूनिट परीक्षण "पूरी तरह से भयानक", "वास्तव में अच्छा" और "सभी तरह की अच्छी चीजें हैं" लेकिन मेरी फ़ाइलों में 70% या अधिक डेटाबेस एक्सेस (कुछ पढ़े और कुछ लिखते हैं) और मुझे यकीन नहीं है कि कैसे इन फ़ाइलों के लिए एक इकाई परीक्षण लिखने के लिए।

मैं PHP और पायथन का उपयोग कर रहा हूं, लेकिन मुझे लगता है कि यह एक ऐसा सवाल है जो डेटाबेस एक्सेस का उपयोग करने वाली अधिकांश / सभी भाषाओं पर लागू होता है।

जवाबों:


82

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

मॉकिंग के लिए अपनी वस्तुओं को स्थापित करने के लिए, आपको संभवतः नियंत्रण / निर्भरता इंजेक्शन पैटर्न के कुछ उलट उपयोग करने की आवश्यकता है, जैसा कि निम्नलिखित छद्म कोड में है:

class Bar
{
    private FooDataProvider _dataProvider;

    public instantiate(FooDataProvider dataProvider) {
        _dataProvider = dataProvider;
    }

    public getAllFoos() {
        // instead of calling Foo.GetAll() here, we are introducing an extra layer of abstraction
        return _dataProvider.GetAllFoos();
    }
}

class FooDataProvider
{
    public Foo[] GetAllFoos() {
        return Foo.GetAll();
    }
}

अब आपके यूनिट टेस्ट में, आप FooDataProvider का मज़ाक बनाते हैं, जो वास्तव में डेटाबेस को हिट किए बिना आपको GetAllFoos विधि को कॉल करने की अनुमति देता है।

class BarTests
{
    public TestGetAllFoos() {
        // here we set up our mock FooDataProvider
        mockRepository = MockingFramework.new()
        mockFooDataProvider = mockRepository.CreateMockOfType(FooDataProvider);

        // create a new array of Foo objects
        testFooArray = new Foo[] {Foo.new(), Foo.new(), Foo.new()}

        // the next statement will cause testFooArray to be returned every time we call FooDAtaProvider.GetAllFoos,
        // instead of calling to the database and returning whatever is in there
        // ExpectCallTo and Returns are methods provided by our imaginary mocking framework
        ExpectCallTo(mockFooDataProvider.GetAllFoos).Returns(testFooArray)

        // now begins our actual unit test
        testBar = new Bar(mockFooDataProvider)
        baz = testBar.GetAllFoos()

        // baz should now equal the testFooArray object we created earlier
        Assert.AreEqual(3, baz.length)
    }
}

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


मुझे पता है कि यह पुराना है, लेकिन जो पहले से ही डीबी में है, उसके लिए एक डुप्लिकेट तालिका बनाने के बारे में क्या। इस तरह से आप DB कॉल के काम की पुष्टि कर सकते हैं?
ब्रेटनर

1
मैं PHP के पीडीओ को डेटाबेस में मेरे सबसे निचले स्तर तक पहुंच के रूप में उपयोग कर रहा हूं, जिस पर मैंने एक इंटरफ़ेस निकाला है। फिर मैंने उसके ऊपर एक एप्लीकेशन अवेलेबल डेटाबेस लेयर बनाया। यह वह परत है जो सभी कच्चे SQL क्वेरी और अन्य जानकारी रखती है। शेष एप्लिकेशन इस उच्च-स्तरीय डेटाबेस के साथ सहभागिता करता है। मैंने इसे इकाई परीक्षण के लिए बहुत अच्छा काम करने के लिए पाया है; मैं अपने एप्लिकेशन पृष्ठों का परीक्षण करता हूं कि वे एप्लिकेशन डेटाबेस के साथ कैसे इंटरैक्ट करते हैं। मैं अपने एप्लिकेशन डेटाबेस का परीक्षण करता हूं कि यह पीडीओ के साथ कैसे इंटरैक्ट करता है। मुझे लगता है कि बग के बिना पीडीओ काम करता है। स्रोत कोड: manx.codeplex.com
वैध करें

1
@bretterer - डुप्लिकेट तालिका बनाना एकीकरण परीक्षण के लिए अच्छा है। इकाई परीक्षण के लिए आप आमतौर पर एक नकली वस्तु का उपयोग करेंगे जो आपको डेटाबेस की परवाह किए बिना कोड की एक इकाई का परीक्षण करने की अनुमति देगा।
बॉर्नटॉकोड

2
आपके यूनिट परीक्षणों में डेटाबेस कॉल को बाहर करने का क्या मूल्य है? यह उपयोगी नहीं लगता है क्योंकि आप किसी भिन्न परिणाम को वापस करने के लिए कार्यान्वयन को बदल सकते हैं, लेकिन आपकी इकाई परीक्षण (गलत तरीके से) पास होगा।
bmay2

2
@ bmay2 तुम गलत नहीं हो। मेरा मूल उत्तर बहुत समय पहले (9 वर्ष!) तब लिखा गया था जब बहुत से लोग अपने कोड को एक परीक्षण योग्य तरीके से नहीं लिख रहे थे, और जब परीक्षण उपकरण में गंभीर कमी थी। मैं अब इस दृष्टिकोण की सिफारिश नहीं करूंगा। आज मैं सिर्फ एक परीक्षण डेटाबेस स्थापित करूँगा और इसे परीक्षण के लिए आवश्यक डेटा के साथ पॉप्युलेट करूँगा, और / या अपने कोड को डिज़ाइन करूँगा ताकि मैं बिना किसी डेटाबेस के यथासंभव तर्क का परीक्षण कर सकूँ।
डग R

25

आदर्श रूप से, आपकी वस्तुओं को लगातार अज्ञानी होना चाहिए। उदाहरण के लिए, आपके पास एक "डेटा एक्सेस लेयर" होनी चाहिए, जिससे आप वस्तुओं को वापस करने के लिए अनुरोध करेंगे। इस तरह, आप अपनी इकाई परीक्षणों से उस हिस्से को छोड़ सकते हैं, या उन्हें अलग-थलग कर सकते हैं।

यदि आपकी वस्तुओं को आपकी डेटा परत पर कसकर जोड़ा जाता है, तो उचित इकाई परीक्षण करना मुश्किल है। इकाई परीक्षण का पहला भाग, "इकाई" है। सभी इकाइयों को अलगाव में परीक्षण करने में सक्षम होना चाहिए।

मेरी c # परियोजनाओं में, मैं पूरी तरह से अलग डेटा परत के साथ NHibernate का उपयोग करता हूं। मेरी वस्तुएँ कोर डोमेन मॉडल में रहती हैं और मेरी एप्लिकेशन लेयर से एक्सेस की जाती हैं। एप्लिकेशन परत डेटा परत और डोमेन मॉडल परत दोनों से बात करती है।

एप्लिकेशन परत को कभी-कभी "बिजनेस लेयर" भी कहा जाता है।

यदि आप PHP का उपयोग कर रहे हैं, तो केवल डेटा एक्सेस के लिए कक्षाओं का एक विशिष्ट सेट बनाएं । सुनिश्चित करें कि आपकी वस्तुओं का कोई पता नहीं है कि वे कैसे बनी रहती हैं और आपके आवेदन वर्गों में दोनों को तार करती हैं।

एक अन्य विकल्प होगा मॉकिंग / स्टब्स का उपयोग करना।


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

3
यदि आपकी वस्तुओं को आपकी डेटा परत पर कसकर जोड़ा जाता है, तो उचित इकाई परीक्षण करना मुश्किल है। इकाई परीक्षण का पहला भाग, "इकाई" है। सभी इकाइयों को अलगाव में परीक्षण करने में सक्षम होना चाहिए। अच्छी व्याख्या
अमिताभ 9:15

11

डेटाबेस एक्सेस के साथ किसी ऑब्जेक्ट को यूनिट टेस्ट करने का सबसे आसान तरीका लेन-देन स्कोप है।

उदाहरण के लिए:

    [Test]
    [ExpectedException(typeof(NotFoundException))]
    public void DeleteAttendee() {

        using(TransactionScope scope = new TransactionScope()) {
            Attendee anAttendee = Attendee.Get(3);
            anAttendee.Delete();
            anAttendee.Save();

            //Try reloading. Instance should have been deleted.
            Attendee deletedAttendee = Attendee.Get(3);
        }
    }

यह डेटाबेस की स्थिति को वापस लौटा देगा, मूल रूप से एक लेन-देन रोलबैक की तरह ताकि आप बिना किसी साइडफ़ेक्ट के जितनी बार चाहें परीक्षण चला सकें। हमने बड़ी परियोजनाओं में इस दृष्टिकोण का सफलतापूर्वक उपयोग किया है। हमारे निर्माण को चलाने में (15 मिनट) थोड़ा लंबा समय लगता है, लेकिन 1800 यूनिट परीक्षण के लिए यह भयानक नहीं है। इसके अलावा, अगर निर्माण समय एक चिंता का विषय है, तो आप कई बिल्ड बनाने के लिए बिल्ड प्रक्रिया को बदल सकते हैं, एक src के निर्माण के लिए, दूसरा वह जो बाद में फायर करता है जो यूनिट टेस्ट, कोड विश्लेषण, पैकेजिंग, आदि को संभालता है ...


1
+1 बहुत समय बचाता है जब यूनिट डेटा एक्सेस लेयर्स का परीक्षण करती है। बस ध्यान दें कि टीएस को अक्सर
एमएसडीटीसी की

मूल प्रश्न PHP के बारे में था, यह उदाहरण C # प्रतीत होता है। वातावरण बहुत अलग हैं।
वैध

2
प्रश्न के लेखक ने कहा कि यह उन सभी भाषाओं पर लागू होने वाला एक सामान्य प्रश्न है जिनका DB के साथ कुछ करना है।
वेदरन

9
और इस प्यारे दोस्तों, को एकीकरण परीक्षण कहा जाता है
एए।

10

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

हमने पहले एक अमूर्त परत बनाई, जिसने हमें किसी भी उचित डेटाबेस कनेक्शन में "स्लॉट" करने की अनुमति दी (हमारे मामले में, हमने बस एक ओडीबीसी-प्रकार कनेक्शन का समर्थन किया)।

एक बार जब यह जगह थी, तब हम अपने कोड में कुछ ऐसा करने में सक्षम थे (हम C ++ में काम करते हैं, लेकिन मुझे यकीन है कि आपको यह विचार प्राप्त होगा):

GetDatabase ()। ExecuteSQL ("INSERT INTO foo (blah, blah)")

सामान्य समय में, GetDatabase () एक ऐसी वस्तु लौटाएगा जो हमारे सभी sql (क्वेरीज़ सहित) को सीधे ODBC के माध्यम से डेटाबेस में फीड कर देगी।

हमने तब इन-मेमोरी डेटाबेस को देखना शुरू किया - एक लंबे रास्ते से सबसे अच्छा SQLite लगता है। ( http://www.sqlite.org/index.html )। यह सरल रूप से सेट अप और उपयोग करने के लिए सरल है, और हमें उप-वर्ग और गेट-वेबेस को ओवरराइड करने की अनुमति देता है, जो कि प्रत्येक परीक्षण के लिए बनाए गए और नष्ट हो चुके डेटाबेस में SQL को अग्रेषित करता है।

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

कुल मिलाकर, इसने हमारी TDD प्रक्रिया के साथ बहुत मदद की है, क्योंकि कुछ कीड़े को ठीक करने के लिए काफी सहज बदलाव जैसा लगता है, जो आपके सिस्टम के अन्य क्षेत्रों (पता लगाने में मुश्किल) पर बहुत अजीब प्रभाव डालता है - बहुत ही सामान्य प्रकृति के वर्ग / डेटाबेस के कारण।

जाहिर है, हमारे अनुभव सी + + विकास के वातावरण के आसपास केंद्रित हैं, हालांकि मुझे यकीन है कि आप शायद पीएचपी / पायथन के तहत कुछ इसी तरह का काम कर सकते हैं।

उम्मीद है की यह मदद करेगा।


9

यदि आप यूनिट को अपनी कक्षाओं का परीक्षण करना चाहते हैं तो आपको डेटाबेस एक्सेस का मखौल उड़ाना चाहिए। आखिरकार, आप एक यूनिट टेस्ट में डेटाबेस का परीक्षण नहीं करना चाहते हैं। यह एक एकीकरण परीक्षण होगा।

कॉल को दूर करें और फिर एक नकली डालें जो अपेक्षित डेटा लौटाता है। यदि आपकी कक्षाएं प्रश्नों को निष्पादित करने से अधिक नहीं करती हैं, तो यह उनके परीक्षण के लायक भी नहीं हो सकता है, हालांकि ...


6

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


4

आपके पास विकल्प:

  • इकाई परीक्षण शुरू करने से पहले डेटाबेस को मिटा देगा एक स्क्रिप्ट लिखें, फिर डेटा के पूर्वनिर्धारित सेट के साथ db को आबाद करें और परीक्षण चलाएं। आप यह भी कर सकते हैं कि हर परीक्षा से पहले - यह धीमा होगा, लेकिन कम त्रुटि वाला।
  • डेटाबेस को इंजेक्ट करें। (छद्म-जावा में उदाहरण, लेकिन सभी OO- भाषाओं पर लागू होता है)

    वर्ग डेटाबेस {
     सार्वजनिक परिणाम क्वेरी (स्ट्रिंग क्वेरी) {... यहां वास्तविक db ...}
    }

    वर्ग MockDatabase डेटाबेस का विस्तार सार्वजनिक परिणाम क्वेरी (स्ट्रिंग क्वेरी) { वापसी "नकली परिणाम"; } }

    वर्ग ObjectThatUsesDB { सार्वजनिक ObjectThatUsesDB (डेटाबेस डीबी) { this.database = db; } }

    अब उत्पादन में आप सामान्य डेटाबेस का उपयोग करते हैं और सभी परीक्षणों के लिए आप सिर्फ नकली डेटाबेस को इंजेक्ट करते हैं जिसे आप तदर्थ बना सकते हैं।

  • अधिकांश कोड में DB का उपयोग न करें (वैसे भी यह एक बुरा अभ्यास है)। एक "डेटाबेस" ऑब्जेक्ट बनाएं जो परिणामों के साथ लौटने के बजाय सामान्य वस्तुओं को लौटाएगा (अर्थात Userएक टपल के बजाय वापस आ जाएगा {name: "marcin", password: "blah"}) अपने सभी परीक्षणों को तदर्थ निर्मित वास्तविक वस्तुओं के साथ लिखें और एक बड़ा परीक्षण लिखें जो एक डेटाबेस पर निर्भर करता है जो इस रूपांतरण को सुनिश्चित करता है ठीक काम करता है।

बेशक ये दृष्टिकोण पारस्परिक रूप से अनन्य नहीं हैं और आप आवश्यकतानुसार उन्हें मिला सकते हैं और मेल कर सकते हैं।


3

यदि आपके प्रोजेक्ट में उच्च सामंजस्य और ढीली युग्मन है, तो आपके डेटाबेस एक्सेस का परीक्षण करना काफी आसान है। इस तरह आप केवल उन चीजों का परीक्षण कर सकते हैं जो प्रत्येक विशेष वर्ग एक बार में सब कुछ परीक्षण किए बिना करता है।

उदाहरण के लिए, यदि आप अपने यूजर इंटरफेस क्लास का परीक्षण करते हैं, तो आपके द्वारा लिखे गए परीक्षण केवल उस फ़ंक्शन के पीछे काम किए गए यूआई के अंदर तर्क को सत्यापित करने का प्रयास करना चाहिए, न कि व्यावसायिक तर्क या डेटाबेस फ़ंक्शन के पीछे।

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

व्यक्तिगत रूप से मेरे लिए इकाई परीक्षण की छिपी शक्ति यह रही है कि यह मुझे अपने अनुप्रयोगों को उनके बिना बेहतर तरीके से डिजाइन करने के लिए मजबूर करता है। ऐसा इसलिए है क्योंकि इससे मुझे "इस फ़ंक्शन को सब कुछ करना चाहिए" मानसिकता से अलग होने में मदद मिली।

क्षमा करें, मेरे पास PHP / Python के लिए कोई विशिष्ट कोड उदाहरण नहीं हैं, लेकिन यदि आप एक .NET उदाहरण देखना चाहते हैं, तो मेरे पास एक पोस्ट है जो एक ऐसी तकनीक का वर्णन करता है जिसका उपयोग मैं इसी परीक्षण के लिए करता था।


2

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

जब तक मैं डेटा एक्सेस के हिस्से का मजाक नहीं उड़ाना शुरू करता, तब तक मुझे यूनिट परीक्षणों को लिखने में निराशा होती थी, इसलिए मुझे मक्खी पर परीक्षण डेटा बनाने या परीक्षण डेटा बनाने की आवश्यकता नहीं थी। डेटा को मॉक करके आप इसे सभी रन टाइम पर जेनरेट कर सकते हैं और सुनिश्चित करें कि आपकी वस्तुएं ज्ञात इनपुट के साथ ठीक से काम करें।


2

मैंने इसे पीएचपी में कभी नहीं किया है और मैंने पायथन का उपयोग कभी नहीं किया है, लेकिन आप जो करना चाहते हैं वह डेटाबेस में कॉल का मजाक उड़ाते हैं। यह करने के लिए कि आप कुछ IoC लागू कर सकते हैं चाहे 3rd पार्टी टूल या आप इसे स्वयं प्रबंधित करते हैं, तो आप डेटाबेस कॉलर के कुछ नकली संस्करण को लागू कर सकते हैं, जहां आप उस नकली कॉल के परिणाम को नियंत्रित करेंगे।

आईओसी का एक सरल रूप सिर्फ इंटरफेस को कोड करके किया जा सकता है। इसके लिए आपके कोड में किसी प्रकार की ऑब्जेक्ट ओरिएंटेशन की आवश्यकता होती है, इसलिए यह आपके लागू होने पर लागू नहीं हो सकता है (मैं कहता हूं कि जब से मुझे जाना है, PHP और Python का आपका उल्लेख है)

आशा है कि उपयोगी है, अगर और कुछ नहीं आप कुछ शर्तों पर अब खोज करने के लिए मिल गया है।


2

मैं पहली पोस्ट से सहमत हूं - डेटाबेस एक्सेस को एक डीएओ परत में छीन लिया जाना चाहिए जो एक इंटरफ़ेस को लागू करता है। फिर, आप DAO परत के स्टब कार्यान्वयन के खिलाफ अपने तर्क का परीक्षण कर सकते हैं।


2

आप डेटाबेस इंजन को अमूर्त करने के लिए नकली फ्रेमवर्क का उपयोग कर सकते हैं । मुझे नहीं पता कि PHP / Python को कुछ मिला है लेकिन टाइप की गई भाषाओं (C #, Java आदि) के लिए बहुत सारे विकल्प हैं

यह इस बात पर भी निर्भर करता है कि आपने उन डेटाबेस एक्सेस कोड को कैसे डिज़ाइन किया है, क्योंकि कुछ डिज़ाइन यूनिट टेस्ट के लिए अन्य की तुलना में आसान हैं जैसे कि पहले के पोस्ट ने उल्लेख किया है।


2

यूनिट परीक्षणों के लिए परीक्षण डेटा सेट करना एक चुनौती हो सकती है।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.