Bir testin başka bir testin kurulduğu yerde testler nasıl yapılandırılır?


18

Ben sadece ortak API'leri kullanarak bir sistemi test entegrasyon . Ben böyle bir şey görünüyor bir test var:

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

Temel olarak, tek bir işlemin tüm "akışını" test etmeye çalışıyorum. Akıştaki her adım, bir sonraki adımın başarılı olmasına bağlıdır. Kendimi harici API ile kısıtladığım için, değerleri veritabanına alamıyorum.

Yani, ya “A; assert; B; assert; ° C; iddia ediyorum ... "ya da her test yönteminin bir şey yapmadan önce önceki testin sonuçlarına ihtiyaç duyduğu ayrı test yöntemlerine ayırıyorum:

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.

Bence bu kokuyor. Bu testleri yazmanın daha iyi bir yolu var mı?

Yanıtlar:


10

Bu testin sık sık yapılması amaçlanıyorsa , endişeleriniz test sonuçlarını bu sonuçlarla çalışması beklenenlere uygun bir şekilde nasıl sunacağınıza odaklanmalıdır .

Bu açıdan testAllTheThingsbüyük bir kırmızı bayrak kaldırıyor. Bu testi her saatte veya daha sık (buggy kod tabanına karşı, elbette yeniden çalıştırmanın bir anlamı olmayacaktır) ve FAILhangi aşamada başarısız olduğunu net bir şekilde göstermeden her seferinde aynı şeyi gördüğünü düşünün .

Ayrı yöntemler çok daha çekici görünüyor, çünkü yeniden çalışmaların sonuçları (koddaki hataları düzeltmede sürekli ilerleme varsayalım) şöyle görünebilir:

    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

Yan not, geçmiş projelerimden birinde, o kadar çok bağımlı testin yeniden yapıldığı, kullanıcıların daha sonraki aşamada tekrarlanan beklenen hataları, bir önceki başarısızlık tarafından "tetiklendiğini" görmek istemediğinden bile şikayet etmeye başladı. Bu çöpün test sonuçlarını analiz etmelerini zorlaştırdıklarını söylediler "gerisini biliyoruz ki gerisi test tasarımı ile başarısız olacak, bizi tekrar etmekten rahatsız olmayın" .

Sonuç olarak, test geliştiricileri sonunda ek SKIPdurumlarla çerçevelerini genişletmek ve bağımlı testlerin yürütülmesini durdurmak için test yöneticisi koduna bir özellik eklemek ve SKIPped test sonuçlarını rapordan çıkarma seçeneğini eklemek zorunda kaldı:

    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
okuduğum gibi, aslında bir testAllTheThings yazmak daha iyi olurdu gibi görünüyor, ama nerede başarısız olduğunu net bir rapor ile.
Javier

2
@Javier , nerede başarısız olduğu hakkında net bir rapor teorik olarak kulağa hoş geliyor, ancak benim pratiğimde, testler sık ​​sık yapıldığında, bunlarla çalışanlar aptal PASS-FAIL tokenlerini
gnat

7

Test kodunu kurulum kodundan ayırırdım. Belki:

# 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

Unutmayın, üretilen tüm rasgele bilgiler başarısız olması durumunda iddiaya dahil edilmelidir , aksi takdirde testiniz tekrarlanamayabilir. Kullanılan rastgele tohumu bile kaydedebilirim. Ayrıca rastgele bir durum başarısız olduğunda, regresyonu önlemek için bu girdiyi sabit kodlu test olarak ekleyin.


1
+1 sizin için! Testler koddur ve DRY testte üretimde olduğu kadar geçerlidir.
DougM

2

Çok daha iyi değil , ancak en azından kurulum kodunu kod belirtmekten ayırabilirsiniz. Hikayenin tamamını adım adım anlatan ayrı bir yöntem yazın ve kaç adım atması gerektiğini kontrol eden bir parametre alın. Sonra her test simulate 4ya da benzeri bir şey söyleyebilir simulate 10ve sonra ne test ederse onu iddia edebilir.


1

Peki, burada "hava kodlaması" ile Python sözdizimini alamayabilir, ama sanırım fikir olsun: böyle genel bir işlevi uygulayabilirsiniz:

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

testlerinizi şu şekilde yazmanıza izin verir:

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

Tabii ki, bu yaklaşımın okunabilirliğindeki kaybın kullanılmaya değer olup olmadığı tartışmalıdır, ancak kazan plakası kodunu biraz azaltır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.