मुख्य आईडीई: हम एक्सेल अभिनेताओं को काफी जटिल व्यापार तर्क के साथ यूनिट परीक्षण (या फिर कारक की सुविधा के लिए फिर से कारक) कैसे कर सकते हैं?मक्का के बिना अक्का इकाई परीक्षण रणनीतियों
मैं अपनी कंपनी (कुछ बहुत ही बुनियादी चीजें उत्पादन में है) के लिए अक्का का उपयोग कर रहा हूं और अकका टेस्टकिट के साथ अपने अभिनेताओं को फिर से फैक्टरिंग कर रहा हूं ताकि यह देख सके कि मैं इसे सही कर सकता हूं या नहीं। ..
असल में, मैंने जो पढ़ा है वह मैंने आकस्मिक रूप से किया है "मैन, आपको केवल टेस्टकिट चाहिए। यदि आप मॉक्स का उपयोग कर रहे हैं तो आप इसे गलत कर रहे हैं !!" हालांकि दस्तावेज़ और उदाहरण इतने हल्के हैं कि मुझे कई प्रश्न मिलते हैं जो कवर नहीं होते हैं (मूल रूप से उनके उदाहरण बहुत प्रतिस्पर्धी कक्षाएं हैं जिनके पास 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 अधिक वर्बोज़ यह छोटे-से-चरम-जिम्मेदार अभिनेताओं के लिए बहुत सारे बॉयलरप्लेट के साथ हो सकता था। अनिवार्य रूप से, कोड परीक्षण संरचना के चारों ओर लिखा जाएगा।
मैं अति-इंजीनियर परीक्षणों के बारे में भी बहुत सतर्क हूं, क्योंकि यदि वे विधि कॉल के बजाय जटिल संदेश अनुक्रमों का परीक्षण कर रहे हैं (जो मुझे यकीन नहीं है कि मैक्स के अलावा सरल अर्थपूर्ण कॉल की अपेक्षा कैसे करें) परीक्षण सफल हो सकते हैं लेकिन जीते कोर विधि कार्यक्षमता के वास्तव में एक वास्तविक इकाई परीक्षण नहीं है। इसके बजाय यह कोड (या संदेश उत्तीर्ण प्रणाली) में नियंत्रण प्रवाह संरचनाओं का प्रत्यक्ष प्रतिबिंब होगा।
तो शायद यह है कि मैं बहुत अधिक यूनिट परीक्षण पूछ रहा हूं, लेकिन कृपया अगर आपके पास कुछ ज्ञान है तो मुझे सीधे सेट करें!
आपके परीक्षण बहुत दानेदार हो सकते हैं। टेस्ट के लिए, और, एक तरह से, दस्तावेज़ व्यापार तर्क के लिए परीक्षण करना चाहिए। इसलिए, एक तरीका यह है कि आपकी सेवा प्रदान करने वाले अनुबंध के खिलाफ परीक्षण लिखना है। यूनिट परीक्षण जो विशेष कार्यान्वयन विवरणों का परीक्षण करने के लिए लिखे गए हैं, वे रेफैक्टरिंग को मुश्किल बना सकते हैं। आप अपने यूनिट परीक्षणों को ठीक करने में अधिकतर समय व्यतीत कर सकते हैं। –
सुझाव के लिए धन्यवाद, मुझे लगता है कि एक तरह से आप सही हो सकते हैं। हालांकि, यूनिट परीक्षणों की उपयोगिता की मेरी समझ यह है कि यदि आप अपने आवेदन को उचित इकाइयों में तोड़ देते हैं तो उनमें से प्रत्येक का अर्थ कुछ अर्थपूर्ण अर्थ हो सकता है और अलगाव में परीक्षण किया जा सकता है। फिर, बाहरी एकाग्रता को कवर करने के लिए कुछ परीक्षण होने पर भी कोडबेस को अत्यधिक विश्वसनीय माना जा सकता है (क्योंकि वे सभी इकाइयों की कुछ हद तक रैखिक रचनाएं होंगी)। और कई मामलों में (जैसे lib या API बनाना) परीक्षण करने के लिए बाहरी एकीकरण नहीं होगा (ये सभी उपयोगकर्ता कोड हैं) ताकि आपके पास वास्तव में कुछ भी नहीं है लेकिन इकाइयां हैं। – jm0
तो मेरा क्यूनाड्री पुनः: अक्का में परीक्षण वास्तव में है कि मैं इकाई परीक्षण करना चाहता हूं (बाहरी विधि परीक्षण में बहुत अधिक संयोजक डेटा/संदेश-पासिंग शामिल है) लेकिन मैं चाहता हूं कि यूनिट परीक्षणों में अर्थपूर्ण मूल्य हो (मनमाने ढंग से दानेदार नहीं, जैसा कि मैंने उल्लेख किया है टिप्पणी बंद करने में)। – jm0