Python birim testleri nereye gidiyor?


490

Bir kütüphane veya uygulama yazıyorsanız, birim test dosyaları nereye gider?

Test dosyalarını ana uygulama kodundan ayırmak güzeldir, ancak bunları uygulama kök dizininin içindeki bir "testler" alt dizinine koymak gariptir, çünkü test edeceğiniz modülleri içe aktarmayı zorlaştırır.

Burada en iyi uygulama var mı?


4
Bu sorunun birçok kabul edilebilir cevabı vardır ve bu da bir Soru-Cevap sitesi için bir soru değildir. İnsanların SO hakkında tüm yararlı soruların sorulmayacağını anlamaları gerekir. Kalıpçılar bunun için seçiyor ve kurallarına uymalıyız.
Wouter J

121
İnsanlar ayrıca SO sayfalarının bir Google araması için en çok hit olduğu zaman, belki de SO resmi doktrinine bağlı kalmaktan ziyade akışla gitmenin daha iyi olduğunu anlamalıdır.
Christopher Barber

6
@ChristopherBarber insanlar da kararlar ve agresif kapanış bir site olarak SO korkunç itibar kazanmak sonuçlanan ne olduğunu anlamak gerekir Ben gerçekten, gerçekten daha iyi bir seçenek yoksa gitmek istiyorum soru sormak istiyorum.
Stefano Borini

Yanıtlar:


200

Bir dosya için module.py, test_module.pyPythonic adlandırma kurallarına göre birim testi normal olarak çağrılmalıdır .

Koymak için yaygın olarak kabul edilen birkaç yer vardır test_module.py:

  1. İle aynı dizinde module.py.
  2. In ../tests/test_module.py(kod dizini ile aynı düzeyde).
  3. In tests/test_module.py(kod dizininin altında bir seviye).

Testleri bulma ve içe aktarma kolaylığı için # 1'i tercih ediyorum. Hangi yapı sistemini kullanırsanız kullanın, başlayarak dosyaları çalıştıracak şekilde kolayca yapılandırılabilir test_. Aslında, test keşfi için kullanılan varsayılan unittestörüntüdürtest*.py .


12
Load_tests protokol varsayılan olarak deney * .py adlı dosyaları arar. Ayrıca, bu en iyi Google sonucu ve bu unittest belgelerinin her ikisi de test_module.py biçimini kullanır.
dfarrell07

6
'Test_' ile başlayan bir grup dosyanız olmadığından, foo_test.py kullanmak sekmeyi tamamlarken bir veya iki tuşa basabilir.
ardıç

11
@juniper, düşüncelerinizi takiben module_test kodlama sırasında otomatik tamamlamada görünür. Bu kafa karıştırıcı veya sinir bozucu olabilir.
Medeiros

11
Kodu dağıtırken, testleri üretim konumlarımıza yerleştirmek istemiyoruz. Bu nedenle, onları './tests/test_blah.py' bir dizinde bulundurmak, konuşlandırdığımızda sıralamak kolaydır. AYRICA, bazı testler örnek veri dosyaları alır ve bir test dizinine sahip olmak, test verilerini dağıtırsak hayati önem taşır.
Kevin J. Rice

1
@ KevinJ.Rice Kodun üretim yerlerinizde çalıştığını test etmemelisiniz?
endolit

67

Sadece 1 test dosyası

Yalnızca 1 test dosyası varsa, üst düzey bir dizine koymanız önerilir:

module/
    lib/
        __init__.py
        module.py
    test.py

Testi CLI'de çalıştırın

python test.py

Birçok test dosyası

Çok sayıda test dosyası varsa, bir testsklasöre koyun :

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

if __name__ == '__main__':
    unittest.main()

Testi CLI'de çalıştırın

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

kullanım unittest discovery

unittest discovery tüm testi paket klasöründe bulacaktır.

Bir oluşturma __init__.pyiçinde tests/klasörün

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

Testi CLI'de çalıştırın

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

Referans

Birim test çerçevesi


51

Yaygın bir uygulama, test dizinini modül / paketinizle aynı üst dizine koymaktır. Modülünüz foo.py olarak adlandırılmışsa, dizin düzeniniz şöyle görünür:

parent_dir/
  foo.py
  tests/

Tabii ki bunu yapmanın tek bir yolu yok. Ayrıca, bir test alt dizini oluşturabilir ve mutlak içe aktarmayı kullanarak modülü içe aktarabilirsiniz .

Testlerinizi nereye koyarsanız yapın, onları çalıştırmak için burun kullanmanızı tavsiye ederim . Burun , testleriniz için dizinlerinizde arama yapar. Bu şekilde, organizasyon açısından en anlamlı oldukları yere testler koyabilirsiniz.


16
Bunu yapmak istiyorum ama çalışmasını sağlayamıyorum. Testleri çalıştırmak için parent_dir dizinindeyim ve şunu yazın: "python testing \ foo_test.py" ve foo_test.py: "from ..foo bunu içe aktarın, bu, daha sonra" Şununla başarısız olur: "ValueError: Deneme "parent_dir ve testlerin her birinde bir init .py var , bu yüzden neden paket olmadıklarından emin değilim. Çünkü komut satırından çalıştırmak üst düzey komut dosyası bir init .py ile bir dir içinde olsa bile, bir paket (parçası) olarak kabul edilemez çünkü şüpheli . Peki testleri nasıl yaparım? Bugün Windows üzerinde çalışıyorum, pamuklu çoraplarımı korusun.
Jonathan Hartley

4
Birim testlerini çalıştırmanın en iyi yolu kütüphane / programınızı kurmak, ardından burun ile birim testleri yapmaktır. Bunu çok daha kolay hale getirmek için virtualenv ve virtualenvwrapper'ı tavsiye ederim.
Cristian

@Tartley - abartılı ithalatların çalışması için 'testler' dizininizde bir init .py dosyasına ihtiyacınız var . Burunla çalışan bu yöntem var, bu yüzden neden sorun yaşadığınızdan emin değilim.
cmcginty

4
Teşekkürler Casey - ama tüm ilgili dizinlerde bir init .py dosyam var . Neyi yanlış yaptığımı bilmiyorum, ama tüm Python projelerimde bu sorun var ve neden kimsenin yapmadığını anlayamıyorum. Ah canım.
Jonathan Hartley

8
Sorunum için bir çözüm, Python2.6 veya daha yeni bir sürümle, python -m project.package.tests.module_tests (python projesi / paket / testler / module_tests.py yerine) . Bu, test modülünün dizinini yola koyar, böylece testler test edilen modülü almak için üst dizinlerine göreli bir içe aktarma yapabilir.
Jonathan Hartley

32

Python programları için birim testleri oluşturan Pythoscope ( https://pypi.org/project/pythoscope/ ) yazarken de aynı soruyu sorduk . Bir dizin seçmeden önce insanları python listesindeki testlerle sorguladık, birçok farklı görüş vardı. Sonunda kaynak kod ile aynı dizine bir "test" dizini koymayı seçtik. Bu dizinde, üst dizindeki her modül için bir test dosyası oluştururuz.


2
Müthiş! Sadece Pythoscope (büyük logo), bazı eski kod üzerinde koştu ve şaşırtıcıydı. Anında en iyi uygulamalar ve diğer geliştiricilerin artık başarısız olan test saplamalarını doldurmalarını sağlayabilirsiniz. Şimdi bambuya takmak için mi? :)
Will

27

Ayrıca, test fonksiyonlarını ana gövdeye koymamaya, ancak her şeyi bir

if __name__ == '__main__':
   do tests...

blok. Bu, test ettiğiniz python dosyasının nasıl kullanılacağına dair dosyaya 'örnek kod' olarak dokümantasyon ekler.

Eklemeliyim, çok sıkı modüller / sınıflar yazma eğilimindeyim. Modülleriniz çok sayıda test gerektiriyorsa, bunları başka bir teste koyabilirsiniz, ancak o zaman bile yine de ekleyeceğim:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

Bu, kaynak kodunuzu okuyan herkesin test kodunu nerede arayacağını bilmesini sağlar.


"Jeremy Cantrell notların üstünde" nerede?
endolith

Bu şekilde çalışmayı seviyorum. En basit düzenleyiciler dosyanızı bir kısayol tuşuyla çalıştıracak şekilde yapılandırılabilir, böylece kodu görüntülerken testleri anında çalıştırabilirsiniz. Ne yazık ki Python dışındaki dillerde bu korkunç görünebilir, bu yüzden bir C ++ veya Java mağazasındaysanız iş arkadaşlarınız bu konuda kaşlarını çatabilir. Ayrıca kod kapsamı araçlarıyla da iyi çalışmaz.
Keeely

19

Her seferinde kendimi test yerleştirme konusunu kontrol ederken buluyorum ve çoğunluk kütüphane kodunun yanında ayrı bir klasör yapısı önerdiğinde, ancak argümanların her seferinde aynı ve ikna edici olmadığını görüyorum. Sonunda test modülümü çekirdek modüllerin yanında bir yere koyuyorum.

Bunu yapmanın ana nedeni: yeniden düzenleme .

Bir şeyleri hareket ettirdiğimde test modüllerinin kodla birlikte hareket etmesini istiyorum; ayrı bir ağaçtalarsa testleri kaybetmek kolaydır. Dürüst olalım, er ya da geç django , flask ve diğerleri gibi tamamen farklı bir klasör yapısı ile sonuçlanırsınız . Umursamıyorsan iyi olur.

Kendinize sormanız gereken ana soru şudur:

Yazıyor muyum:

  • a) yeniden kullanılabilir kütüphane veya
  • b) Bazı yarı ayrılmış modülleri bir araya getirerek bir proje oluşturmak?

Eğer bir:

Ayrı bir klasör ve yapısını korumak için fazladan çaba daha uygun olabilir. Hiç kimse testlerin üretime alınmasından şikayet etmeyecek .

Ancak, çekirdek klasörlerle karıştırıldığında testlerin dağıtılmasını engellemek de kolaydır; bunu setup.py dosyasına koyun :

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

B ise:

Her birimizin yaptığı gibi, yeniden kullanılabilir kütüphaneler yazmanızı isteyebilirsiniz, ancak çoğu zaman yaşamları projenin hayatına bağlıdır. Projenizi kolayca devam ettirebilmek bir öncelik olmalıdır.

Daha sonra iyi bir iş yaptıysanız ve modülünüz başka bir proje için uygunsa, muhtemelen bu yeni projeye kopyalanır - çatallanamaz veya ayrı bir kütüphaneye dönüştürülmez ve aynı klasör yapısında yan yana duran testler taşınır. ayrı bir test klasörü haline gelmiş bir karmaşa içinde balıkçılık kadar kıyasla kolaydır. (İlk etapta karışıklık olmaması gerektiğini savunabiliriz, ama burada gerçekçi olalım).

Bu yüzden seçim hala sizindir, ancak karışık testlerle ayrı bir klasörle aynı şeyleri elde ettiğinizi, ancak işleri düzenli tutmak için daha az çaba harcadığınızı iddia ediyorum.


1
Testlerin üretime dağıtılmasındaki sorun nedir aslında? benim deneyimime göre, bu çok yararlı: kullanıcıların gerçekten kendi ortamlarında test yapmalarına izin verir ... hata raporları aldığınızda, kullanıcıdan her şeyin yolunda olduğundan emin olmak ve test için sıcak yamalar göndermesini sağlamak için test paketini çalıştırmasını isteyebilirsiniz. Eğer bu module.tests da testleri koymak çünkü paketi doğrudan ... ayrıca, öyle değil edecek Eğer kurulum dosyasında şey yanlış yaptım sürece, üretimde sona ...
anarcat

2
Cevabımda yazdığım gibi, genellikle ayrı testler yapmam. Fakat. Testleri dağıtım paketine koymak, üretim envinde (örneğin, bazı modül seviyesi kodu) normalde istemediğiniz şeylerin yürütülmesine yol açabilir. Elbette testlerin nasıl yazıldığına bağlıdır, ancak onları güvenli bir şekilde bırakmak için kimse yanlışlıkla zararlı bir şey çalıştırmayacaktır. Testleri dağıtımlara dahil etmeme karşı değilim, ama genel bir kural olarak daha güvenli olduğunu anlıyorum. Ve onları ayrı bir klasöre koymak, onları dist içine dahil etmemek süper kolay hale getirir.
Janusz Skonieczny

15

Bir tests/dizin kullanın ve sonra göreli ithalatları kullanarak ana uygulama modülleri almak. Uygulamam / testler / foo.py'de şunlar olabilir:

from .. import foo

MyApp.foomodülü almak için .


5
"ValueError: Pakette olmayan göreceli içe aktarma girişimi"
cprn

12

Yerleşik bir "en iyi uygulama" olduğuna inanmıyorum.

Testlerimi uygulama kodunun dışındaki başka bir dizine koydum. Daha sonra tüm testleri çalıştırmadan önce test çalıştırıcısı komut dosyamda (bu da başka şeyler de yapıyor) sys.path'e (modülleri herhangi bir yerden içe aktarmanıza izin veriyor) ana uygulama dizinini ekledim. Bu şekilde, test dizinini serbest bıraktığımda ana koddan kaldırmak zorunda kalmam, bana çok az miktarda zaman ve çaba kazandırır.


3
Daha sonra ana uygulama dizinini sys.path'e ekliyorum Sorun, testlerinizde bazı yardımcı modüller (test web sunucusu gibi) içeriyorsa ve bu yardımcı modülleri uygun testlerinizden içe aktarmanız gerektiğinde ortaya çıkar.
Piotr Dobrogost

Benim için böyle görünüyor:os.sys.path.append(os.dirname('..'))
Matt

11

Python'da Test çerçeveleri geliştirme deneyimimden, python birim testlerini ayrı bir dizine koymanızı öneririm. Simetrik dizin yapısını koruyun. Bu, yalnızca çekirdek kütüphanelerin paketlenmesinde yardımcı olur ve birim testlerini paketlemez. Aşağıda şematik bir diyagram ile uygulanmaktadır.

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

Bu şekilde, bu kütüphaneleri bir rpm kullanarak paketlediğinizde, sadece ana kütüphane modüllerini paketleyebilirsiniz (sadece). Bu, özellikle çevik ortamda korunmaya yardımcı olur.


1
Bu yaklaşımın potansiyel bir avantajının, testlerin kendi bağımsız bir uygulaması olarak geliştirilebileceğini (hatta belki de versiyon kontrollü) olduğunu fark ettim. Tabii ki, her avantaj için bir dezavantaj vardır - simetriyi korumak temelde "çift giriş muhasebesi" yapmaktır ve yeniden işlenmeyi daha fazla angarya haline getirir.
Jasha

Yumuşak takip sorusu: Çevik ortam neden bu yaklaşıma özellikle uygundur? (yani çevik iş akışı, simetrik bir dizin yapısını bu kadar faydalı kılar?)
Jasha

11

GitHub'daki bazı ana Python projelerini kontrol etmenizi ve bazı fikirler edinmenizi öneririm.

Kodunuz büyüdükçe ve daha fazla kitaplık eklediğinizde, setup.py ile aynı dizinde bir test klasörü oluşturmak ve her bir test türü (proje testi, entegrasyon, ...) için proje dizini yapınızı yansıtmak daha iyidir.

Örneğin, aşağıdaki gibi bir dizin yapınız varsa:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

Test klasörü ekledikten sonra aşağıdaki gibi bir dizin yapınız olacaktır:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

Düzgün yazılmış birçok Python paketi aynı yapıyı kullanır. Çok iyi bir örnek Boto paketidir. Https://github.com/boto/boto adresini kontrol edin


1
"Test" yerine "test" kullanılması tavsiye edilir, çünkü "test" bir Python yapıcı modülüdür. docs.python.org/2/library/test.html
brod

Her zaman değil .. mesela matplotlibaltına alır matplotlib/lib/matplotlib/tests( github.com/matplotlib/matplotlib/tree/... ), sklearnaltına sahip scikitelearn/sklearn/tests( github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests )
alpha_989

7

Nasıl yaparım...

Klasör yapısı:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py, projeler modüllerini içeren konum olarak src / işaretini gösterir, sonra çalıştırırım:

setup.py develop

Bu da projemi site paketlerine ekleyerek çalışma kopyamı işaret ediyor. Testlerimi çalıştırmak için şunu kullanıyorum:

setup.py tests

Hangi test çalıştırıcısını yapılandırdığımı kullanarak.


"Modül" terimini aşmış görünüyorsunuz. Çoğu Python programcısı muhtemelen modülün sizin aradığınız dosya olduğunu düşünür code.py. Üst düzey dizini "proje" olarak adlandırmak daha mantıklı olacaktır.
blokeley

4

Üst düzey testler dizinini tercih ederim. Bu, ithalatın biraz daha zor hale geldiği anlamına gelir. Bunun için iki çözümüm var:

  1. Kurulum araçlarını kullanın. Sonra geçebilir test_suite='tests.runalltests.suite'içine setup()ve sadece testleri çalıştırabilirsiniz:python setup.py test
  2. Testleri çalıştırırken PYTHONPATH ayarlayın: PYTHONPATH=. python tests/runalltests.py

Bu şeyler M2Crypto'daki kod tarafından nasıl desteklenir:

Nosetestlerle test yapmayı tercih ederseniz, biraz farklı bir şey yapmanız gerekebilir.


3

Kullanırız

app/src/code.py
app/testing/code_test.py 
app/docs/..

Her test dosyasında biz eklemek ../src/içinde sys.path. En güzel çözüm değil ama işe yarıyor. Birisinin Java'da maven gibi bir şey bulması, hangi projede çalışırsanız çalışın, sadece çalışan standart sözleşmeler veren harika olacağını düşünüyorum.


1

Testler basitse, bunları doktora yerleştirin - Python için test çerçevelerinin çoğu bunu kullanabilecek:

>>> import module
>>> module.method('test')
'testresult'

Daha fazla dahil olan diğer testler için, bunları içeri ../tests/test_module.pyveya içeri koyardım tests/test_module.py.


1

C #, genellikle testleri ayrı bir derleme ayırdım.

Python'da - şimdiye kadar - testin bir fonksiyonun öğretisinde olduğu doctests yazma ya if __name__ == "__main__"da modülün altındaki bloğa koyma eğilimindeydim .


0

"Foo" adlı bir paket yazarken, birim testlerini ayrı bir "foo_test" paketine koyacağım. Modüller ve alt paketler SUT paket modülü ile aynı ada sahip olacaktır. Örneğin foo.xy modülü için testler foo_test.xy'de bulunur. Her test paketinin __init__.py dosyaları, paketin tüm test paketlerini içeren bir AllTests paketi içerir. setuptools, ana test paketini belirtmek için uygun bir yol sağlar, böylece "python setup.py geliştirildikten" sonra "python setup.py testi" veya "python setup.py test -s foo_test.x.SomeTestSuite" sadece belirli bir süit.


0

Testlerimi test edilen kodla aynı dizine koydum (CUT); çünkü foo.pytestler içinde foo_ut.pyveya benzeri olacaktır. (Bunları bulmak için test keşif işlemini değiştirdim.)

Bu, testleri bir dizin listesindeki kodun hemen yanına yerleştirerek testlerin orada olduğunu açıkça gösterir ve testleri ayrı bir dosyadayken olabildiğince kolay açmayı sağlar. (Komut satırı editörleri için,vim foo* ve bir grafik dosya sistemi tarayıcısı kullanırken, önce CUT dosyasını ve ardından hemen bitişikteki test dosyasını tıklamanız yeterlidir.)

Diğerlerinin işaret ettiği gibi , bu, gerektiğinde başka bir yerde kullanım için kodu yeniden düzenlemeyi ve çıkarmayı da kolaylaştırır.

Testleri tamamen farklı bir dizin ağacına koyma fikrinden gerçekten hoşlanmıyorum; neden geliştiricilerin CUT ile dosyayı açtıklarında testleri açmasını zorlaştırıyorsunuz? Geliştiricilerin büyük çoğunluğu, testleri yazmak veya ayarlamak için çok istekli değiller, bariyeri bir bahane olarak kullanmak yerine herhangi bir engeli görmezden geleceklerdir. (Tam tersi, deneyimlerime göre; mümkün olduğunca kolay hale getirdiğinizde bile, test yazma zahmetine giremeyen birçok geliştirici biliyorum.)


-2

Yakın zamanda Python'da program yapmaya başladım, bu yüzden henüz en iyi uygulamayı bulma şansım olmadı. Ancak, tüm testleri bulan ve çalıştıran bir modül yazdım.

Yani, var:

Uygulamanın /
 appfile.py
Ölçek/
 appfileTest.py

Daha büyük projelere ilerlerken nasıl gittiğini görmek zorundayım.


4
Merhaba, camelCase python dünyasında biraz garip.
Stuart Axon
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.