2016-02-27 4 views
7

मुख्य आईडीई: हम एक्सेल अभिनेताओं को काफी जटिल व्यापार तर्क के साथ यूनिट परीक्षण (या फिर कारक की सुविधा के लिए फिर से कारक) कैसे कर सकते हैं?मक्का के बिना अक्का इकाई परीक्षण रणनीतियों

मैं अपनी कंपनी (कुछ बहुत ही बुनियादी चीजें उत्पादन में है) के लिए अक्का का उपयोग कर रहा हूं और अकका टेस्टकिट के साथ अपने अभिनेताओं को फिर से फैक्टरिंग कर रहा हूं ताकि यह देख सके कि मैं इसे सही कर सकता हूं या नहीं। ..

असल में, मैंने जो पढ़ा है वह मैंने आकस्मिक रूप से किया है "मैन, आपको केवल टेस्टकिट चाहिए। यदि आप मॉक्स का उपयोग कर रहे हैं तो आप इसे गलत कर रहे हैं !!" हालांकि दस्तावेज़ और उदाहरण इतने हल्के हैं कि मुझे कई प्रश्न मिलते हैं जो कवर नहीं होते हैं (मूल रूप से उनके उदाहरण बहुत प्रतिस्पर्धी कक्षाएं हैं जिनके पास 1 विधि & कोई अन्य अभिनेता नहीं है, या केवल विधि के अंत में इनपुट आउटपुट जैसे छोटे तरीकों से बातचीत करता है)। एक तरफ के रूप में, अगर कोई मुझे किसी भी उचित जटिलता के साथ एक एक्क्का ऐप के लिए टेस्ट सूट पर इंगित कर सकता है, तो मैं वास्तव में इसकी सराहना करता हूं।

यहां मैं कम से कम कुछ विशिष्ट मामलों का विवरण देने का प्रयास करूंगा & जानना चाहेंगे कि कोई "अक्का-प्रमाणित" दृष्टिकोण कहलाएगा (लेकिन कृपया कुछ भी अस्पष्ट नहीं है ... मैं रोलैंड कुह्न शैली पद्धति की तलाश में हूं , अगर वह वास्तव में विशिष्ट मुद्दों के लिए वास्तव में गहराई से गोता लगाने के लिए थे)। मैं उन रणनीतियों को स्वीकार करूंगा जिनमें रिफैक्टरिंग शामिल है, लेकिन कृपया परिदृश्यों में उल्लिखित मेरी चिंताओं को ध्यान दें।

परिदृश्य 1: पार्श्व विधियों (विधि एक ही अभिनेता में एक और बुला)

case class GetProductById(id : Int) 
case class GetActiveProductsByIds(ids : List[Int]) 

class ProductActor(val partsActor : ActorRef, inventoryActor : ActorRef) extends Actor { 
    override def receive: Receive = { 
    case GetProductById(pId) => pipe(getProductById(pId)) to sender 
    case GetActiveProductsByIds(pIds) => pipe(getActiveProductsByIds(pIds)) to sender 
    } 

    def getProductById(id : Int) : Future[Product] = { 
    for { 
     // Using pseudo-code here 
     parts <- (partsActor ? GetPartsForProduct(id)).mapTo[List[Part]] 
     instock <- (inventoryActor ? StockStatusRequest(id)).mapTo[Boolean] 
     product <- Product(parts, instock) 
    } yield product 
    } 

    def getActiveProductsByIds(ids : List[Int]) : Future[List[Product]] = { 
    for { 
     activeProductIds <- (inventoryActor ? FilterActiveProducts(ids)).mapTo[List[Int]] 
     activeProducts <- Future.sequence(activeProductIds map getProductById) 
    } yield activeProducts 
    } 
} 

तो, मूल रूप से यहाँ हम अनिवार्य रूप से 2 लाने रहे हैं जो विधि है, एक विलक्षण और कई के लिए एक। एकवचन मामले में, परीक्षण सरल है। हमने एक टेस्टएक्टररफ स्थापित किया है, कन्स्ट्रक्टर में कुछ जांच इंजेक्ट करें और केवल यह सुनिश्चित कर लें कि सही संदेश श्रृंखला फायरिंग हो रही है।

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

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

एक तरफ: इनलाइन (स्थिर) तर्क

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

object ProductActor { 
    def passthroughToysOnly(products : List[Product]) : List[Toy] = 
    products flatMap {p => 
     p.category match { 
     case "toy" => Some(p) 
     case _ => None 
     } 
    } 
} 

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

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

कहाँ यह सब वास्तव में मेरे लिए टूट जाती है यहाँ है -

परिदृश्य 2: रिकर्सिव एल्गोरिदम

case class GetTypeSpecificProductById(id : Int) 

class TypeSpecificProductActor(productActor : ActorRef, bundleActor : ActorRef) extends Actor { 
    override def receive: Receive = { 
    case GetTypeSpecificProductById(pId) => pipe(getTypeSpecificProductById(pId)) to sender 
    } 

    def getTypeSpecificProductById(id : Int) : Future[Product] = { 
    (productActor ? GetProductById(id)).mapTo[Product] flatMap (p => p.category match { 
     case "toy" => Toy(p.id, p.name, p.color) 
     case "bundle" => 
      Bundle(p.id, p.name, 
      getProductsInBundle((bundleActor ? GetProductIdsForBundle(p.id).mapTo[List[Int])) 
     } 
    ) 
    } 

    def getProductsInBundle(ids : List[Int]) : List[Product] = 
    ids map getProductById 
} 

तो हाँ यहाँ स्यूडोकोड का एक सा है, लेकिन सार अब वह यह है कि हम एक रिकर्सिव विधि है (getProductId एक बंडल के मामले में उत्पाद प्रोडक्ट्स प्राप्त करने के लिए बुला रहा है, जो फिर से GetProductId को कॉल कर रहा है)। मजाक करने के साथ, ऐसे मुद्दे हैं जहां हम चीजों को और अधिक टेस्ट करने योग्य बनाने के लिए रिकर्सन काट सकते हैं। लेकिन यह तथ्य इस तथ्य के कारण जटिल है कि विधि में कुछ पैटर्न-मैचों के भीतर अभिनेता कॉल करता है।

यह वास्तव में मेरे लिए एकदम सही तूफान है .... निचले अभिनेता में "बंडल" मामले के लिए मैच निकालने का वादा किया जा सकता है, लेकिन फिर भी इसका मतलब है कि अब हमें परिपत्र निर्भरता से निपटने की जरूरत है (बंडलएस्प्लोरर्स अभिनेता की जरूरत टाइप स्पेसिफिकएक्टर जिसे बंडलएस्प्लर की जरूरत है ...)।

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

टिप्पणियां

धन्यवाद किसी भी मदद के लिए समय से आगे! मैं वास्तव में कम से कम, टेस्टेबल, अच्छी तरह से डिज़ाइन किए गए कोड के बारे में बहुत भावुक हूं और मुझे डर है कि अगर मैं निष्कर्षण के माध्यम से सब कुछ हासिल करने का प्रयास करता हूं तो मुझे अभी भी परिपत्र संबंधी समस्याएं होंगी, फिर भी किसी भी इनलाइन/संयोजक तर्क का परीक्षण करने में सक्षम नहीं हैं & मेरा कोड होगा 1000x अधिक वर्बोज़ यह छोटे-से-चरम-जिम्मेदार अभिनेताओं के लिए बहुत सारे बॉयलरप्लेट के साथ हो सकता था। अनिवार्य रूप से, कोड परीक्षण संरचना के चारों ओर लिखा जाएगा।

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

तो शायद यह है कि मैं बहुत अधिक यूनिट परीक्षण पूछ रहा हूं, लेकिन कृपया अगर आपके पास कुछ ज्ञान है तो मुझे सीधे सेट करें!

+0

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

+0

सुझाव के लिए धन्यवाद, मुझे लगता है कि एक तरह से आप सही हो सकते हैं। हालांकि, यूनिट परीक्षणों की उपयोगिता की मेरी समझ यह है कि यदि आप अपने आवेदन को उचित इकाइयों में तोड़ देते हैं तो उनमें से प्रत्येक का अर्थ कुछ अर्थपूर्ण अर्थ हो सकता है और अलगाव में परीक्षण किया जा सकता है। फिर, बाहरी एकाग्रता को कवर करने के लिए कुछ परीक्षण होने पर भी कोडबेस को अत्यधिक विश्वसनीय माना जा सकता है (क्योंकि वे सभी इकाइयों की कुछ हद तक रैखिक रचनाएं होंगी)। और कई मामलों में (जैसे lib या API बनाना) परीक्षण करने के लिए बाहरी एकीकरण नहीं होगा (ये सभी उपयोगकर्ता कोड हैं) ताकि आपके पास वास्तव में कुछ भी नहीं है लेकिन इकाइयां हैं। – jm0

+0

तो मेरा क्यूनाड्री पुनः: अक्का में परीक्षण वास्तव में है कि मैं इकाई परीक्षण करना चाहता हूं (बाहरी विधि परीक्षण में बहुत अधिक संयोजक डेटा/संदेश-पासिंग शामिल है) लेकिन मैं चाहता हूं कि यूनिट परीक्षणों में अर्थपूर्ण मूल्य हो (मनमाने ढंग से दानेदार नहीं, जैसा कि मैंने उल्लेख किया है टिप्पणी बंद करने में)। – jm0

उत्तर

1

मैं आपके बयान से असहमत हूं "मैं उन तर्कों से दूर रखने का बड़ा प्रशंसक नहीं हूं जो उनका उपयोग करते हैं"।

मुझे यह इकाई परीक्षण और कोड संगठन का एक आवश्यक घटक माना जाता है।

यह कुछ अतिरिक्त लाभ है:

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

जब कोड लिखने मैं इसे एक कदम अपने उदाहरण से आगे ले और एक अलग पैकेज में व्यापार तर्क के लिए कदम:

package businessLogic 

object ProductGetter { 
    def passthroughToysOnly(products : List[Product]) : List[Toy] = 
    products flatMap {p => 
     p.category match { 
     case "toy" => Some(p) 
     case _ => None 
    } 
    } 
} 

यह वायदा, जावा धागे, या यहाँ तक के लिए संगामिति कार्यप्रणाली को बदलने के लिए अनुमति देता है कुछ ने अभी तक मेरे व्यापार तर्क को दोबारा नहीं किए बिना समवर्ती पुस्तकालय बनाया है। व्यापार तर्क पैकेज कोड के "क्या" बन जाते हैं, अक्का पुस्तकालय "कैसे" बन जाते हैं।

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

अपनी विशिष्ट समस्या को संबोधित करते हुए: मैं अभिनेता से getActiveProductsByIds हटा दूंगा। यदि अभिनेता का उपयोगकर्ता केवल सक्रिय उत्पादों को प्राप्त करना चाहता है तो इसे पहले आईडी को फ़िल्टर करने के लिए छोड़ दें। आपके अभिनेता को केवल 1 चीज करना चाहिए: GetProductById। एलन फिर से हवाला देते हुए:

यह एक अभिनेता के अलावा कार्य करने के बनाने के लिए बहुत आसान है - हम बस यह काम का अधिक से विभिन्न प्रकार के प्रदर्शन करने के लिए अनुमति देने के लिए अपने प्राप्त ब्लॉक करने के लिए नए संदेश जोड़ें। हालांकि, ऐसा करने से आपकी क्षमता अभिनेताओं की रचना प्रणाली को सीमित करती है और प्रासंगिक समूह को परिभाषित करती है। अपने कलाकारों को एक ही प्रकार के काम पर केंद्रित रखें, और ऐसा करने में, स्वयं को लचीले ढंग से उपयोग करने की अनुमति दें।

+0

मैं व्यापार तर्क को दूर करने के बारे में आपके बयान से सहमत हूं। असल में, मेरे असली एप्लिकेशन तर्क में टाइप फ़िल्टरिंग जैसे उत्पाद ऑब्जेक्ट पर ही किया जाता है (या किसी अन्य अभिनेता में यदि इसमें अधिक अभिनेता इंटरैक्शन शामिल होते हैं)। कई मुद्दों को इंगित करने के लिए परिदृश्य छद्म कोड बनाते हैं, मैं मानता हूं। मैं मानता हूं कि एक फ़िल्टरिंग fetch एक अलग अभिनेता में संबंधित है, लेकिन यदि आप उस विधि को कोई फ़िल्टर नहीं सोचते हैं, तो समस्या बनी हुई है। यदि मैं इसे बहु-fetch का परीक्षण करने के लिए बस ले जाता हूं तो ऐसे कई कलाकार होंगे (ProductCollectorActor, ProductFilterActor)। कक्षाओं को अनिवार्य रूप से केवल परीक्षणों की सुविधा के लिए। – jm0

+0

संक्षेप में, इन्हें कई अभिनेताओं में रखने से कोई "अभिनेता प्रणाली" शैली लाभ नहीं होगा, यह कुछ छोटे डीएओ कॉलआउट को पूरा करने के लिए कई अभिनेता परतों के साथ एक भारी ऐप है। – jm0

+1

@ jm0 मेरा प्राथमिक बिंदु यह था कि यदि आपके पास अलग-अलग व्यवसाय तर्क हैं और इसे पूरी तरह से जांचें तो अभिनेता परीक्षण अपेक्षाकृत सरल हो सकते हैं, मूल रूप से प्राप्त विधि पर जांच परीक्षण टाइप करें। –

0

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

दूसरे दिन मैं reading about this था, और एक सुझाव मिला कि मैंने पहले कोशिश नहीं की थी: observer pattern का उपयोग कर। विचार है कि अपने अभिनेताओं को केवल मैसेजिंग की देखभाल करने के लिए छोड़ दें (जिसे आपको परीक्षण करने की आवश्यकता नहीं है, अक्का टीम आपके लिए यह करेगी;) और ग्राहकों को ईवेंट प्रसारित करें। इस तरह आपका तर्क अभिनेताओं से पूरी तरह अलग हो जाता है, इसलिए परीक्षण करना बहुत आसान हो जाता है।

नोट: मैंने उत्पादन प्रणाली में यह कोशिश नहीं की है, लेकिन चूंकि आपने उल्लेख किया है कि आपके उत्पादन प्रणाली में केवल मूलभूत सामग्री ही एक शॉट के लायक हो सकती है।

संबंधित मुद्दे