วิธีจัดโครงสร้างการทดสอบโดยที่การทดสอบครั้งหนึ่งเป็นการตั้งค่าการทดสอบอื่น?


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...

โดยพื้นฐานแล้วฉันกำลังพยายามทดสอบ "การไหล" ทั้งหมดของธุรกรรมเดียว แต่ละขั้นตอนในการไหลขึ้นอยู่กับขั้นตอนก่อนหน้านี้ที่ประสบความสำเร็จ เนื่องจากฉัน จำกัด ตัวเองกับ API ภายนอกฉันจึงไม่สามารถไปหาค่าในฐานข้อมูล

ดังนั้นไม่ว่าฉันจะมีวิธีการทดสอบที่ยาวจริงๆซึ่งก็คือ `A; ยืนยัน; B; ยืนยัน; ค; ยืนยัน ... "หรือฉันแบ่งมันออกเป็นวิธีการทดสอบแยกซึ่งวิธีการทดสอบแต่ละวิธีต้องใช้ผลลัพธ์ของการทดสอบก่อนหน้านี้ก่อนที่มันจะทำสิ่งนั้น:

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
ขณะที่ฉันอ่านมันดูเหมือนว่าจะเป็นการดีกว่าถ้าจะเขียนการทดสอบ AllTheThings แต่ด้วยการรายงานที่ชัดเจนว่ามันล้มเหลวที่ไหน
Javier

2
@Javier การรายงานที่ชัดเจนว่ามันล้มเหลวฟังดูดีในทางทฤษฎี แต่ในทางปฏิบัติของฉันเมื่อใดก็ตามที่การทดสอบถูกดำเนินการบ่อยผู้ที่ทำงานกับสิ่งเหล่านี้อย่างยิ่งชอบที่จะเห็นสัญญาณโทเค็น
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 จะใช้ในการทดสอบมากพอ ๆ กับที่ใช้ในการผลิต
DougM

2

ไม่ดีกว่ามากแต่อย่างน้อยคุณสามารถแยกรหัสการตั้งค่าออกจากการยืนยันรหัสได้ เขียนวิธีแยกต่างหากที่บอกเล่าเรื่องราวทีละขั้นตอนและใช้พารามิเตอร์ควบคุมจำนวนขั้นตอนที่ควรทำ จากนั้นการทดสอบแต่ละครั้งสามารถพูดสิ่งที่ชอบsimulate 4หรือsimulate 10จากนั้นยืนยันสิ่งที่มันทดสอบ


1

ฉันอาจไม่ได้รับ Python syntax ที่นี่ด้วย "air coding" แต่ฉันเดาว่าคุณจะได้รับแนวคิด: คุณสามารถใช้ฟังก์ชั่นทั่วไปเช่นนี้:

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)
  # ...

แน่นอนมันเป็นที่ถกเถียงกันหากการสูญเสียความสามารถในการอ่านของวิธีนี้มีค่าที่จะใช้ แต่จะลดรหัสสำเร็จรูปเล็กน้อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.