संरचना परीक्षण कैसे करें जहां एक परीक्षण दूसरे परीक्षण का सेटअप है?


18

मैं एकीकरण एक प्रणाली का परीक्षण, केवल सार्वजनिक API का उपयोग करके। मेरे पास एक परीक्षण है जो कुछ इस तरह दिखता है:

def testAllTheThings():
  email = create_random_email()
  password = create_random_password()

  ok = account_signup(email, password)
  assert ok
  url = wait_for_confirmation_email()
  assert url
  ok = account_verify(url)
  assert ok

  token = get_auth_token(email, password)
  a = do_A(token)
  assert a
  b = do_B(token, a)
  assert b
  c = do_C(token, b)

  # ...and so on...

मूल रूप से, मैं एकल लेनदेन के संपूर्ण "प्रवाह" का परीक्षण करने का प्रयास कर रहा हूं। प्रवाह में प्रत्येक चरण पिछले चरण के सफल होने पर निर्भर करता है। क्योंकि मैं खुद को बाहरी एपीआई तक सीमित कर रहा हूं, मैं सिर्फ डेटाबेस में मूल्यों को पोक नहीं कर सकता।

तो, या तो मेरे पास वास्तव में एक लंबी परीक्षण विधि है जो `ए; ज़ोर; बी; ज़ोर; सी; मुखर ... ", या मैं इसे अलग-अलग परीक्षण विधियों में तोड़ता हूं, जहां प्रत्येक परीक्षण विधि को पिछले परीक्षण के परिणामों की आवश्यकता होती है, इससे पहले कि यह हो सके:

def testAccountSignup():
  # etc.
  return email, password

def testAuthToken():
  email, password = testAccountSignup()
  token = get_auth_token(email, password)
  assert token
  return token

def testA():
  token = testAuthToken()
  a = do_A(token)
  # etc.

मुझे लगता है कि यह बदबू आ रही है। क्या इन परीक्षणों को लिखने का एक बेहतर तरीका है?

जवाबों:


10

यदि इस परीक्षण को बार-बार चलाने का इरादा है , तो आपकी चिंताओं पर ध्यान केंद्रित किया जाएगा कि कैसे इन परिणामों के साथ काम करने के लिए उन लोगों के लिए सुविधाजनक तरीके से परीक्षा परिणाम प्रस्तुत किया जाए ।

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

अलग-अलग विधियां बहुत अधिक आकर्षक लगती हैं, क्योंकि फिर से रन के परिणाम (कोड में बग्स को ठीक करने में स्थिर प्रगति) के परिणाम निम्नानुसार दिख सकते हैं:

    FAIL FAIL FAIL FAIL
    PASS FAIL FAIL FAIL -- 1st stage fixed
    PASS FAIL FAIL FAIL
    PASS PASS FAIL FAIL -- 2nd stage fixed
    ....
    PASS PASS PASS PASS -- we're done

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

परिणामस्वरूप, परीक्षण डेवलपर्स को अंततः अतिरिक्त SKIPस्थिति के साथ अपने ढांचे का विस्तार करने और परीक्षण प्रबंधक कोड में एक सुविधा जोड़ने के लिए मजबूर किया गया था, जो कि आश्रित परीक्षणों के निष्पादन को रद्द करने और SKIPरिपोर्ट से पैड परीक्षण के परिणामों को छोड़ने का एक विकल्प है , ताकि यह देखा जाए:

    FAIL -- the rest is skipped
    PASS FAIL -- 1st stage fixed, abort after 2nd test
    PASS FAIL
    PASS PASS FAIL -- 2nd stage fixed, abort after 3rd test
    ....
    PASS PASS PASS PASS -- we're done

1
जैसा कि मैंने इसे पढ़ा, ऐसा लगता है कि यह वास्तव में एक परीक्षण लिखने के लिए बेहतर होता है। थिंग्स, लेकिन स्पष्ट रिपोर्टिंग के साथ जहां वह विफल रहा है।
जेवियर

2
@ जेवियर की स्पष्ट रिपोर्टिंग जहां यह सिद्धांत रूप में अच्छी लगती है, लेकिन मेरे अभ्यास में, जब भी परीक्षणों को अक्सर निष्पादित किया जाता है, तो जो लोग इन के साथ काम करते हैं वे दृढ़ता से गूंगा पास-फेल टोकन
gnat

7

मैं परीक्षण कोड को सेटअप कोड से अलग करूंगा। शायद:

# Setup
def accountSignup():
    email = create_random_email()
    password = create_random_password()

    ok = account_signup(email, password)
    url = wait_for_confirmation_email()
    verified = account_verify(url)
    return email, password, ok, url, verified

def authToken():
    email, password = accountSignup()[:2]
    token = get_auth_token(email, password)
    return token

def getA():
    token = authToken()
    a = do_A()
    return a

def getB():
    a = getA()
    b = do_B()
    return b

# Testing
def testAccountSignup():
    ok, url, verified = accountSignup()[2:]
    assert ok
    assert url
    assert verified

def testAuthToken():
    token = authToken()
    assert token

def testA():
    a = getA()
    assert a

def testB():
    b = getB()
    assert b

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


1
आपके लिए +1! परीक्षण कोड हैं, और DRY परीक्षण में उतना ही लागू होता है जितना उत्पादन में।
डगएम

2

नहीं ज्यादा बेहतर है, लेकिन आप कम से कम सेटअप कोड को कोड डालने से अलग कर सकते हैं। एक अलग विधि लिखें, जो पूरी कहानी को चरण दर चरण बताती है, और एक पैरामीटर को नियंत्रित करती है कि इसे कितने कदम उठाने चाहिए। फिर प्रत्येक परीक्षण कुछ ऐसा कह सकता है simulate 4या simulate 10फिर जो कुछ भी परीक्षण करता है उसे मुखर करता है।


1

ठीक है, मुझे शायद "एयर कोडिंग" द्वारा पायथन सिंटैक्स यहां नहीं मिलेगा, लेकिन मुझे लगता है कि आपको यह विचार मिलता है: आप इस तरह से एक सामान्य फ़ंक्शन को लागू कर सकते हैं:

def asserted_call(create_random_email,*args):
    var result=create_random_email(*args)
    assert result
    return result

जो आपको इस तरह अपने परीक्षण लिखने की अनुमति देगा:

  asserted_call(account_signup, email, password)
  url = asserted_call(wait_for_confirmation_email)
  asserted_call(account_verify,url)
  token = asserted_call(get_auth_token,email, password)
  # ...

बेशक, यह बहस का मुद्दा है अगर इस दृष्टिकोण की पठनीयता में कमी इसका उपयोग करने के लायक है, लेकिन यह बॉयलरप्लेट कोड को थोड़ा कम करता है।

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