2010-09-13 9 views
8

एक स्टब/मॉक करने के लिए कब और कब नहीं जब मैं टीडीडी/बीडीडी विकास पैटर्न की ओर बढ़ने के लिए अपने सिर को रुपेक के चारों ओर लपेटने के लिए एक ठोस प्रयास कर रहा हूं। हालांकि, मैं कुछ बुनियादी सिद्धांतों से दूर हूं और संघर्ष कर रहा हूं:एक परीक्षण

पसंद है, जब मुझे मोक्स/स्टब्स का उपयोग करना चाहिए और मुझे कब नहीं करना चाहिए? उदाहरण के लिए

लें इस परिदृश्य: मैं एक Site मॉडल है कि has_many :blogs और Blog मॉडल has_many :articles है। मेरे Site मॉडल में मेरे पास एक कॉलबैक फ़िल्टर है जो प्रत्येक नई साइट के लिए ब्लॉग और आलेखों का एक डिफ़ॉल्ट सेट बनाता है। मैं उस कोड का परीक्षण करना चाहता हूं, इसलिए यहां जाता है:

describe Site, "when created" do 

    include SiteSpecHelper 

    before(:each) do 
    @site = Site.create valid_site_attributes 
    end 

    it "should have 2 blogs" do 
    @site.should have(2).blogs 
    end 

    it "should have 1 main blog article" do 
    @site.blogs.find_by_slug("main").should have(1).articles 
    end 

    it "should have 2 secondary blog articles" do 
    @site.blogs.find_by_slug("secondary").should have(2).articles 
    end 

end 

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

describe Site, "when created" do 

    include SiteSpecHelper 

    before(:each) do 
    site = Site.new 
    @blog = Blog.new 
    @article = Article.new 
    Site.stub!(:create).and_return(site) 
    Blog.stub!(:create).and_return(@blog) 
    Article.stub!(:create).and_return(@article) 
    @site = Site.create valid_site_attributes 
    end 

    it "should have 2 blogs" do 
    @site.stub!(:blogs).and_return([@blog, @blog]) 
    @site.should have(2).blogs 
    end 

    it "should have 1 main blog article" do 
    @blog.stub!(:articles).and_return([@article]) 
    @site.stub_chain(:blogs, :find_by_slug).with("main").and_return(@blog) 
    @site.blogs.find_by_slug("main").should have(1).articles 
    end 

    it "should have 2 secondary blog articles" do 
    @blog.stub!(:articles).and_return([@article, @article]) 
    @site.stub_chain(:blogs, :find_by_slug).with("secondary").and_return(@blog) 
    @site.blogs.find_by_slug("secondary").should have(2).articles 
    end 

end 

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

अब, या तो मैं पूरी तरह से mocks/स्टब्स की बात नहीं छूटा है, या मैं उसे मौलिक रूप से गलत आ रहा हूँ, लेकिन मैं आशा करती हूं कि किसी को या तो कर सकता है:

  • में सुधार मेरे ऊपर परीक्षण इसलिए यह उन तरीकों से स्टब्स या मैक्स का उपयोग करता है जो वास्तव में मेरे परीक्षणों के बजाए मेरे कोड का परीक्षण करते हैं।
  • या, मुझे बताएं कि मुझे यहां स्टब्स का उपयोग करना चाहिए - या वास्तव में यह पूरी तरह से अनावश्यक है और मुझे इन मॉडलों को परीक्षण डेटाबेस में लिखना चाहिए।

उत्तर

2

लेकिन, मैं अपने परीक्षण की लंबाई दोगुना कर दिया है और पूरी कवायद सिर्फ मुझे के रूप में पूरी तरह से व्यर्थ हमलों, क्योंकि मैं अब मेरी कोड का परीक्षण कर रहा हूँ, मैं बस अपना परीक्षण परीक्षण कर रहा हूँ।

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

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

एक और उदाहरण admin? नामक एक सहायक के लिए है जो वर्तमान में लॉग-इन उपयोगकर्ता एक व्यवस्थापक है या नहीं, इस पर आधारित वास्तविक या गलत देता है। आप Shoulda में देखना चाहते हो सकता है

# helper 
def admin? 
    unless current_user.nil? 
    return current_user.is_admin? 
    else 
    return false 
    end 
end 

# spec 
describe "#admin?" do 
    it "should return false if no user is logged in" do 
    stubs(:current_user).returns(nil) 
    admin?.should be_false 
    end 

    it "should return false if the current user is not an admin" do 
    stubs(:current_user).returns(mock(:is_admin? => false)) 
    admin?.should be_false 
    end 

    it "should return true if the current user is an admin" do 
    stubs(:current_user).returns(mock(:is_admin? => true)) 
    admin?.should be_true 
    end 
end 

एक मध्यम जमीन के रूप में,: मैं एक उपयोगकर्ता लॉगिन faking के माध्यम से जाना नहीं चाहता था, इसलिए मैंने ऐसा किया। इस तरह आप केवल यह सुनिश्चित कर सकते हैं कि आपके मॉडल में परिभाषित है, और भरोसा करें कि रेल का अच्छी तरह से परीक्षण किया गया है कि एसोसिएशन एक संबंधित मॉडल बनाने के बाद "बस काम करेगा" और फिर इसे गिनने के बिना।

मुझे Member नामक एक मॉडल मिला है जो मूल रूप से मेरे ऐप में सब कुछ संबंधित है। इसमें 10 संघ परिभाषित हैं। मैं उन संगठनों में से प्रत्येक का परीक्षण कर सकता है, या मैं सिर्फ ऐसा कर सकता है:

it { should have_many(:achievements).through(:completed_achievements) } 
it { should have_many(:attendees).dependent(:destroy) } 
it { should have_many(:completed_achievements).dependent(:destroy) } 
it { should have_many(:loots).dependent(:nullify) } 
it { should have_one(:last_loot) } 
it { should have_many(:punishments).dependent(:destroy) } 
it { should have_many(:raids).through(:attendees) } 
it { should belong_to(:rank) } 
it { should belong_to(:user) } 
it { should have_many(:wishlists).dependent(:destroy) } 
+0

धन्यवाद, यह एक सहायक प्रतिक्रिया है। :) – aaronrussell

1

यही कारण है कि मैं स्टब्स का उपयोग/बहुत मुश्किल से ही मजाक उड़ाता है (वास्तव में केवल जब मैं एक बाहरी वेब सेवा से टकराने जा करने के लिए जा रहा हूँ)। बचाया गया समय केवल जटिल जटिलता के लायक नहीं है।

आपके परीक्षण समय को तेज करने के बेहतर तरीके हैं, और निक गौथियर एक गुच्छा को कवर करने के लिए एक अच्छी बात देता है - video और slides देखें।

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

+0

धन्यवाद आपकी प्रतिक्रिया और लिंक के लिए। अब वीडियो देख रहे हैं। – aaronrussell

1

मुझे दूसरों पर सहमत होने के साथ इतना यकीन नहीं है। असली समस्या (जैसा कि मैंने इसे देखा), यह है कि आप एक ही परीक्षण (खोज व्यवहार, और सृजन) के साथ दिलचस्प व्यवहार के कई टुकड़ों का परीक्षण कर रहे हैं। यह क्यों बुरा है, इसके कारणों के लिए, इस बात को देखें: http://www.infoq.com/presentations/integration-tests-scam। मैं इस बाकी उत्तर के लिए मान रहा हूं कि आप उस सृजन का परीक्षण करना चाहते हैं जो आप परीक्षण करना चाहते हैं।

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

स्टार्टर्स के लिए, डिज़ाइन से पूछताछ के लिए, Site क्या ब्लॉग में लेख जोड़ें समझ में आता है? Blog पर क्लास विधि के बारे में क्या कुछ Blog.with_one_article कहा जाता है। इसका मतलब यह है कि आपको परीक्षण करना है कि उस वर्ग विधि को दो बार बुलाया गया है (यदि [जैसा कि मैं इसे अभी समझता हूं], आपके पास प्रत्येक Site के लिए "प्राथमिक" और "द्वितीयक" Blog है, और संघ स्थापित किए गए हैं (मुझे रेल में अभी तक ऐसा करने का कोई अच्छा तरीका नहीं मिला है, मैं आमतौर पर इसका परीक्षण नहीं करता)

इसके अलावा, क्या आप Site.create पर कॉल करते समय ActiveRecord की निर्माण विधि को ओवरराइड कर रहे हैं? यदि ऐसा है, तो मैं सुझाव देना चाहता हूं साइट पर एक नई कक्षा विधि ने कुछ और नाम दिया है (Site.with_default_blogs संभवतः?)। यह मेरी सामान्य आदत है, ओवरराइडिंग सामान आमतौर पर परियोजनाओं में समस्याओं का कारण बनता है।

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