Làm cách nào để cấu trúc các bài kiểm tra trong đó một bài kiểm tra là một thiết lập của bài kiểm tra khác?


18

Tôi đang tích hợp kiểm tra một hệ thống, bằng cách chỉ sử dụng các API công khai. Tôi có một bài kiểm tra trông giống như thế này:

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

Về cơ bản, tôi đang cố gắng kiểm tra toàn bộ "dòng chảy" của một giao dịch. Mỗi bước trong luồng phụ thuộc vào bước trước đó thành công. Vì tôi giới hạn bản thân với API bên ngoài, tôi không thể chỉ đưa các giá trị vào cơ sở dữ liệu.

Vì vậy, hoặc tôi có một phương thức thử nghiệm thực sự dài mà `A; khẳng định; B; khẳng định; C; khẳng định ... "hoặc tôi chia nó thành các phương pháp thử nghiệm riêng biệt, trong đó mỗi phương pháp thử nghiệm cần kết quả của thử nghiệm trước đó trước khi có thể thực hiện việc của mình:

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.

Tôi nghĩ rằng mùi này. Có cách nào tốt hơn để viết các bài kiểm tra này?

Câu trả lời:


10

Nếu thử nghiệm này được dự định để chạy thường xuyên , mối quan tâm của bạn sẽ tập trung vào cách trình bày kết quả thử nghiệm theo cách thuận tiện cho những người dự kiến ​​sẽ làm việc với những kết quả này.

Từ quan điểm này, testAllTheThingsgiơ một lá cờ đỏ khổng lồ. Hãy tưởng tượng ai đó đang chạy thử nghiệm này mỗi giờ hoặc thậm chí thường xuyên hơn (tất nhiên là chống lại cơ sở mã lỗi, nếu không sẽ không có điểm nào để chạy lại) và nhìn thấy mọi lúc đều giống nhau FAIL, mà không có dấu hiệu rõ ràng về giai đoạn nào thất bại.

Các phương thức riêng biệt trông hấp dẫn hơn nhiều, vì kết quả của việc chạy lại (giả sử tiến trình ổn định trong việc sửa lỗi trong mã) có thể trông như sau:

    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

Lưu ý bên lề, trong một trong những dự án trước đây của tôi, có rất nhiều lần thử nghiệm phụ thuộc mà người dùng thậm chí bắt đầu phàn nàn về việc không sẵn sàng nhìn thấy những thất bại được mong đợi lặp lại ở giai đoạn sau "gây ra" bởi thất bại ở lần trước. Họ nói rằng rác này khiến họ khó phân tích kết quả kiểm tra hơn "chúng tôi đã biết rằng phần còn lại sẽ thất bại do thiết kế thử nghiệm, đừng làm phiền chúng tôi lặp lại" .

Do đó, các nhà phát triển thử nghiệm cuối cùng đã buộc phải mở rộng khung của họ với SKIPtrạng thái bổ sung và thêm một tính năng trong mã trình quản lý kiểm tra để hủy bỏ việc thực hiện các thử nghiệm phụ thuộc và một tùy chọn để loại bỏ SKIPkết quả thử nghiệm ped khỏi báo cáo, sao cho giống như:

    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
khi tôi đọc nó, có vẻ như sẽ tốt hơn nếu thực sự viết một bài kiểm tra AllTheThings, nhưng với báo cáo rõ ràng về nơi nó thất bại.
Javier

2
@Javier báo cáo rõ ràng về nơi mà nó thất bại nghe có vẻ hay trên lý thuyết, nhưng trong thực tế của tôi, bất cứ khi nào các bài kiểm tra được thực hiện thường xuyên, những người làm việc với những người này thích nhìn thấy các token
gnat

7

Tôi muốn tách mã thử nghiệm khỏi mã thiết lập. Có lẽ:

# 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

Hãy nhớ rằng, tất cả thông tin ngẫu nhiên được tạo phải được đưa vào xác nhận trong trường hợp không thành công, nếu không, thử nghiệm của bạn có thể không thể lặp lại được. Tôi thậm chí có thể ghi lại hạt giống ngẫu nhiên được sử dụng. Ngoài ra, bất cứ khi nào một trường hợp ngẫu nhiên không thành công, hãy thêm đầu vào cụ thể đó dưới dạng thử nghiệm được mã hóa cứng để ngăn hồi quy.


1
+1 cho bạn! Các thử nghiệm là mã và DRY áp dụng nhiều trong thử nghiệm cũng như trong sản xuất.
DougM

2

Không tốt hơn nhiều , nhưng ít nhất bạn có thể tách mã thiết lập khỏi mã xác nhận. Viết một phương thức riêng biệt để kể toàn bộ câu chuyện từng bước và lấy tham số kiểm soát số bước cần thực hiện. Sau đó, mỗi bài kiểm tra có thể nói một cái gì đó như simulate 4hoặc simulate 10sau đó khẳng định bất cứ điều gì nó kiểm tra.


1

Chà, tôi có thể không nhận được cú pháp Python ngay tại đây bằng cách "mã hóa không khí", nhưng tôi đoán bạn có ý tưởng: bạn có thể thực hiện một chức năng chung như thế này:

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

sẽ cho phép bạn viết các bài kiểm tra của bạn như thế này:

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

Tất nhiên, thật đáng tranh luận nếu sự mất khả năng đọc của phương pháp này đáng để sử dụng nó, nhưng nó làm giảm mã nồi hơi một chút.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.