Di mana tes unit Python pergi?


490

Jika Anda menulis perpustakaan, atau aplikasi, ke mana file tes unit ini pergi?

Sangat bagus untuk memisahkan file uji dari kode aplikasi utama, tetapi canggung untuk menempatkannya dalam subdirektori "tes" di dalam direktori root aplikasi, karena akan lebih sulit untuk mengimpor modul yang akan Anda uji.

Apakah ada praktik terbaik di sini?


4
Ada banyak jawaban yang dapat diterima untuk pertanyaan ini dan yang membuatnya bukan pertanyaan untuk situs tanya jawab. Orang-orang perlu memahami bahwa tidak semua pertanyaan bermanfaat dapat diajukan di SO. Para pembentuk memilih untuk itu dan kita harus mengikuti aturan mereka.
Wouter J

121
Orang-orang juga perlu memahami bahwa ketika halaman SO berakhir sebagai hit teratas untuk pencarian Google, bahwa mungkin lebih baik mengikuti arus daripada tetap berpegang pada doktrin resmi SO.
Christopher Barber

6
@ChristopherBarber orang juga perlu memahami bahwa keputusan dan penutupan agresif adalah apa yang akhirnya menghasilkan reputasi yang sangat mengerikan sebagai situs yang saya lebih suka tidak mengajukan pertanyaan kecuali saya benar-benar tidak punya pilihan yang lebih baik.
Stefano Borini

Jawaban:


200

Untuk file module.py, tes unit biasanya dipanggil test_module.py, mengikuti konvensi penamaan Pythonic.

Ada beberapa tempat yang dapat diterima secara umum test_module.py:

  1. Dalam direktori yang sama dengan module.py.
  2. In ../tests/test_module.py(pada level yang sama dengan direktori kode).
  3. In tests/test_module.py(satu tingkat di bawah direktori kode).

Saya lebih suka # 1 karena kesederhanaannya dalam menemukan tes dan mengimpornya. Apa pun sistem pembangunan yang Anda gunakan dapat dengan mudah dikonfigurasi untuk menjalankan file yang dimulai dengan test_. Sebenarnya, pola default unittestyang digunakan untuk penemuan tes adalahtest*.py .


12
The load_tests protokol pencarian untuk file bernama test * py secara default. Selain itu, hasil teratas Google ini dan dokumentasi yang paling tidak lengkap ini menggunakan format test_module.py.
dfarrell07

6
Menggunakan foo_test.py dapat menyimpan satu atau dua penekanan tombol saat menggunakan penyelesaian-tab karena Anda tidak memiliki banyak file yang dimulai dengan 'test_'.
juniper-

11
@juniper, mengikuti pemikiran Anda, module_test akan muncul dalam penyelesaian otomatis saat Anda membuat kode. Itu bisa membingungkan atau mengganggu.
Medeiros

11
Saat menyebarkan kode, kami tidak ingin menyebarkan tes ke lokasi produksi kami. Jadi, memiliki mereka dalam satu direktori './tests/test_blah.py' mudah untuk dicabut ketika kita melakukan penyebaran. JUGA, beberapa tes mengambil file data sampel, dan memilikinya dalam direktori uji sangat penting agar kita menyebarkan data uji.
Kevin J. Rice

1
@ KevinJ.Rice Bukankah Anda seharusnya menguji bahwa kode bekerja di lokasi produksi Anda?
endolith

67

Hanya 1 file uji

Jika hanya memiliki 1 file uji, disarankan untuk menempatkannya di direktori tingkat atas:

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

Jalankan tes di CLI

python test.py

Banyak file uji

Jika memiliki banyak file uji, letakkan di testsfolder:

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()

Jalankan tes di CLI

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

Menggunakan unittest discovery

unittest discovery akan menemukan semua tes di folder paket.

Buat folder __init__.pydalamtests/

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

Jalankan tes di CLI

# In top-level /module/ folder

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

python -m unittest discover

Referensi

Kerangka uji unit


51

Praktik yang umum adalah meletakkan direktori tes di direktori induk yang sama dengan modul / paket Anda. Jadi jika modul Anda disebut foo.py tata letak direktori Anda akan terlihat seperti:

parent_dir/
  foo.py
  tests/

Tentu saja tidak ada cara untuk melakukannya. Anda juga bisa membuat subdirektori tes dan mengimpor modul menggunakan impor absolut .

Di mana pun Anda melakukan tes, saya sarankan Anda menggunakan hidung untuk menjalankannya. Hidung mencari melalui direktori Anda untuk tes. Dengan cara ini, Anda dapat melakukan tes di mana pun mereka membuat paling masuk akal secara organisasi.


16
Saya ingin melakukan ini tetapi saya tidak bisa membuatnya bekerja. Untuk menjalankan tes, saya di parent_dir, dan ketik: "python tests \ foo_test.py", dan di foo_test.py: "from ..foo impor ini, itu, theother" Itu gagal dengan: "ValueError: Mencoba impor relatif dalam bukan paket "Baik parent_dir dan tes memiliki init .py di dalamnya, jadi saya tidak yakin mengapa mereka bukan paket. Saya menduga itu karena skrip tingkat atas yang Anda jalankan dari baris perintah tidak dapat dianggap (bagian dari) paket, bahkan jika itu dalam direktori dengan init .py. Jadi, bagaimana saya menjalankan tes? Saya bekerja pada Windows hari ini, berkat kaus kaki katun saya.
Jonathan Hartley

4
Cara terbaik - saya telah menemukan - untuk menjalankan tes unit adalah menginstal perpustakaan / program Anda kemudian menjalankan tes unit dengan hidung. Saya akan merekomendasikan virtualenv dan virtualenvwrapper untuk membuat ini lebih mudah.
Cristian

@ Tartley - Anda memerlukan file .it init di direktori 'tes' agar impor absolusi berfungsi. Saya memiliki metode ini bekerja dengan hidung, jadi saya tidak yakin mengapa Anda mengalami masalah.
cmcginty

4
Terima kasih Casey - tetapi saya memiliki file .py init di semua direktori yang relevan. Saya tidak tahu apa yang saya lakukan salah, tetapi saya memiliki masalah ini pada semua proyek Python saya dan saya tidak bisa mengerti mengapa tidak ada orang lain yang melakukannya. Oh sayang
Jonathan Hartley

8
Salah satu solusi untuk masalah saya, dengan Python2.6 atau yang lebih baru, kami menjalankan tes dari root proyek Anda menggunakan: python -m project.package.tests.module_tests (alih-alih proyek python / paket / tes / module_tests.py) . Ini menempatkan direktori modul tes di jalur, sehingga tes kemudian dapat melakukan impor relatif ke direktori induk mereka untuk mendapatkan modul-diuji.
Jonathan Hartley

32

Kami memiliki pertanyaan yang sama ketika menulis Pythoscope ( https://pypi.org/project/pythoscope/ ), yang menghasilkan unit test untuk program Python. Kami mensurvei orang-orang pada pengujian dalam daftar python sebelum kami memilih direktori, ada banyak pendapat berbeda. Pada akhirnya kami memilih untuk meletakkan direktori "tes" di direktori yang sama dengan kode sumber. Dalam direktori itu kami membuat file uji untuk setiap modul di direktori induk.


2
Luar biasa! Baru saja menjalankan Pythoscope (logo hebat), pada beberapa kode lawas dan itu luar biasa. Praktik terbaik instan dan Anda bisa meminta pengembang lain untuk mengisi rintisan pengujian yang sekarang gagal. Sekarang untuk menghubungkannya ke bambu? :)
Will

27

Saya juga cenderung untuk menempatkan unit test saya di dalam file itu sendiri, seperti yang dicatat oleh Jeremy Cantrell di atas, meskipun saya cenderung untuk tidak meletakkan fungsi tes di bagian utama, tetapi menempatkan semuanya dalam sebuah

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

blok. Ini akhirnya menambahkan dokumentasi ke file sebagai 'kode contoh' untuk cara menggunakan file python yang Anda uji.

Saya harus menambahkan, saya cenderung menulis modul / kelas yang sangat ketat. Jika modul Anda membutuhkan jumlah tes yang sangat besar, Anda dapat memasukkannya ke tes lain, tetapi meskipun demikian, saya masih akan menambahkan:

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

Ini memungkinkan siapa pun yang membaca kode sumber Anda tahu di mana harus mencari kode uji.


"seperti yang ditulis Jeremy Cantrell di atas" di mana?
endolith

Saya suka cara kerja ini. Editor yang paling sederhana dapat dikonfigurasi untuk menjalankan file Anda dengan hot-key, jadi ketika Anda melihat kode, Anda dapat menjalankan tes dalam sekejap. Sayangnya dalam bahasa selain Python ini bisa terlihat mengerikan, jadi jika Anda berada di toko C ++ atau Java, kolega Anda mungkin tidak menyukai hal ini. Itu juga tidak bekerja dengan baik dengan alat cakupan kode.
Keeely

19

Sesekali saya mendapati diri saya memeriksa topik penempatan tes, dan setiap kali mayoritas merekomendasikan struktur folder terpisah di samping kode perpustakaan, tetapi saya menemukan bahwa setiap kali argumennya sama dan tidak terlalu meyakinkan. Saya akhirnya meletakkan modul tes saya di suatu tempat di samping modul inti.

Alasan utama untuk melakukan ini adalah: refactoring .

Ketika saya memindahkan barang-barang di sekitar saya ingin modul uji untuk bergerak dengan kode; mudah untuk kehilangan tes jika mereka berada di pohon yang terpisah. Mari kita jujur, cepat atau lambat Anda berakhir dengan struktur folder yang sama sekali berbeda, seperti Django , termos dan banyak lainnya. Tidak apa-apa jika Anda tidak peduli.

Pertanyaan utama yang harus Anda tanyakan pada diri sendiri adalah ini:

Apa saya menulis:

  • a) perpustakaan yang dapat digunakan kembali atau
  • b) membangun proyek daripada bundel bersama beberapa modul setengah terpisah?

Jika sebuah:

Folder terpisah dan upaya ekstra untuk mempertahankan strukturnya mungkin lebih cocok. Tidak ada yang akan mengeluh tentang tes Anda yang digunakan untuk produksi .

Tetapi juga mudah untuk mengecualikan tes dari didistribusikan ketika mereka dicampur dengan folder inti; letakkan ini di setup.py :

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

Jika b:

Anda mungkin berharap - seperti yang kita semua lakukan - bahwa Anda menulis perpustakaan yang dapat digunakan kembali, tetapi sebagian besar waktu hidup mereka terkait dengan kehidupan proyek. Kemampuan untuk mempertahankan proyek Anda dengan mudah harus menjadi prioritas.

Kemudian jika Anda melakukan pekerjaan dengan baik dan modul Anda cocok untuk proyek lain, itu mungkin akan disalin - tidak bercabang atau dibuat menjadi perpustakaan yang terpisah - ke dalam proyek baru ini, dan memindahkan tes yang ada di sampingnya di struktur folder yang sama mudah dibandingkan dengan memancing tes dalam kekacauan yang menjadi folder tes terpisah. (Anda mungkin berpendapat bahwa itu seharusnya tidak berantakan sejak awal tetapi mari kita bersikap realistis di sini).

Jadi pilihannya tetap milik Anda, tetapi saya berpendapat bahwa dengan tes campuran Anda mencapai semua hal yang sama dengan folder yang terpisah, tetapi dengan sedikit upaya untuk menjaga hal-hal tetap rapi.


1
apa masalah dengan menggunakan tes untuk produksi, sebenarnya? dalam pengalaman saya, ini sangat berguna: memungkinkan pengguna untuk benar-benar menjalankan tes di lingkungan mereka ... ketika Anda mendapatkan laporan bug, Anda dapat meminta pengguna untuk menjalankan suite tes untuk memastikan semuanya baik-baik saja di sana dan mengirim patch panas untuk pengujian suite langsung ... selain itu, itu bukan karena Anda meletakkan tes Anda di module.tests bahwa itu akan berakhir dalam produksi, kecuali jika Anda melakukan sesuatu yang salah dalam file setup Anda ...
anarcat

2
Seperti yang saya tulis dalam jawaban saya, saya biasanya tidak memisahkan tes. Tapi. Menempatkan tes ke dalam paket distribusi dapat menyebabkan pelaksanaan hal-hal yang biasanya tidak Anda inginkan dalam produksi env (mis. Beberapa kode level modul). Itu tentu saja tergantung pada bagaimana tes ditulis, tetapi untuk berada di sisi aman Anda meninggalkannya, tidak ada yang akan menjalankan sesuatu yang berbahaya karena kesalahan. Saya tidak menentang memasukkan tes dalam distribusi, tetapi saya memahami bahwa sebagai aturan praktis lebih aman. Dan menempatkannya di folder terpisah membuatnya sangat mudah untuk tidak memasukkannya ke dalam dist.
Janusz Skonieczny

15

Saya menggunakan tests/direktori, dan kemudian mengimpor modul aplikasi utama menggunakan impor relatif. Jadi di MyApp / test / foo.py, mungkin ada:

from .. import foo

untuk mengimpor MyApp.foomodul.


5
"ValueError: Mencoba impor relatif dalam bentuk non-paket"
cprn

12

Saya tidak percaya ada "praktik terbaik" yang mapan.

Saya meletakkan tes saya di direktori lain di luar kode aplikasi. Saya kemudian menambahkan direktori aplikasi utama ke sys.path (memungkinkan Anda untuk mengimpor modul dari mana saja) dalam skrip pelari uji saya (yang melakukan beberapa hal lain juga) sebelum menjalankan semua tes. Dengan cara ini saya tidak perlu menghapus direktori tes dari kode utama ketika saya merilisnya, menghemat waktu dan usaha saya, jika jumlah yang sangat kecil.


3
Saya kemudian menambahkan direktori aplikasi utama ke sys.path Masalahnya adalah jika tes Anda mengandung beberapa modul pembantu (seperti server web uji) dan Anda perlu mengimpor modul bantuan tersebut dari dalam tes yang tepat.
Piotr Dobrogost

Inilah yang terlihat bagi saya:os.sys.path.append(os.dirname('..'))
Matt M.

11

Dari pengalaman saya dalam mengembangkan kerangka kerja pengujian di Python, saya akan menyarankan untuk menempatkan tes unit python di direktori yang terpisah. Pertahankan struktur direktori simetris. Ini akan membantu dalam mengemas hanya perpustakaan inti dan tidak mengemas tes unit. Di bawah ini diimplementasikan melalui diagram skematik.

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

Dengan cara ini saat Anda mengemas pustaka-pustaka ini menggunakan rpm, Anda bisa mengemas modul pustaka utama (hanya). Ini membantu pemeliharaan terutama di lingkungan yang gesit.


1
Saya telah menyadari bahwa salah satu keunggulan potensial dari pendekatan ini adalah bahwa tes dapat dikembangkan (dan mungkin bahkan versi yang dikontrol) sebagai aplikasi independen mereka sendiri. Tentu saja, untuk setiap keuntungan ada kerugian - mempertahankan simetri pada dasarnya melakukan "akuntansi entri ganda," dan membuat refactoring lebih dari tugas.
Jasha

Pertanyaan tindak lanjut lunak: mengapa lingkungan yang gesit sangat cocok untuk pendekatan ini? (yaitu, bagaimana dengan alur kerja gesit membuat struktur direktori simetris sangat bermanfaat?)
Jasha

11

Saya sarankan Anda memeriksa beberapa proyek Python utama di GitHub dan mendapatkan beberapa ide.

Ketika kode Anda bertambah besar dan Anda menambahkan lebih banyak pustaka, lebih baik membuat folder uji di direktori yang sama dengan yang Anda setup.py dan mirror struktur direktori proyek Anda untuk setiap jenis tes (unittest, integrasi, ...)

Misalnya jika Anda memiliki struktur direktori seperti:

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

Setelah menambahkan folder uji Anda akan memiliki struktur direktori seperti:

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

Banyak paket Python yang ditulis dengan benar menggunakan struktur yang sama. Contoh yang sangat bagus adalah paket Boto. Periksa https://github.com/boto/boto


1
Disarankan untuk menggunakan "tes" daripada "tes", karena "tes" adalah modul Python build in. docs.python.org/2/library/test.html
brodul

Tidak selalu .. misalnya matplotlibmemilikinya di bawah matplotlib/lib/matplotlib/tests( github.com/matplotlib/matplotlib/tree/… ), sklearnmemilikinya di bawah scikitelearn/sklearn/tests( github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests )
alpha_989

7

Bagaimana saya melakukannya ...

Struktur folder:

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

Setup.py menunjuk ke src / sebagai lokasi yang berisi modul proyek saya, kemudian saya jalankan:

setup.py develop

Yang menambahkan proyek saya ke dalam paket situs, menunjuk ke copy pekerjaan saya. Untuk menjalankan tes saya gunakan:

setup.py tests

Menggunakan test runner mana pun yang saya konfigurasikan.


Anda tampaknya telah menyangkal istilah "modul". Kebanyakan programmer Python mungkin akan berpikir bahwa modul adalah file yang Anda panggil code.py. Akan lebih masuk akal untuk memanggil direktori "proyek" tingkat atas.
blokeley

4

Saya lebih suka direktori tes tingkat atas. Ini berarti impor menjadi sedikit lebih sulit. Untuk itu saya punya dua solusi:

  1. Gunakan alat setup. Maka Anda dapat melewati test_suite='tests.runalltests.suite'ke dalam setup(), dan dapat menjalankan tes sederhana:python setup.py test
  2. Setel PYTHONPATH saat menjalankan tes: PYTHONPATH=. python tests/runalltests.py

Begini caranya hal-hal itu didukung oleh kode dalam M2Crypto:

Jika Anda lebih suka menjalankan tes dengan tes suara, Anda mungkin perlu melakukan sesuatu yang sedikit berbeda.


3

Kita gunakan

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

Dalam setiap file tes kita masukkan ../src/di sys.path. Ini bukan solusi terbaik tetapi berhasil. Saya pikir akan lebih baik jika seseorang datang dengan sesuatu seperti pakar di Jawa yang memberi Anda konvensi standar yang hanya berfungsi, tidak peduli proyek apa yang Anda kerjakan.


1

Jika tesnya sederhana, cukup letakkan di docstring - sebagian besar kerangka kerja pengujian untuk Python akan dapat menggunakannya:

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

Untuk tes lain yang lebih terlibat, saya akan memasukkannya ke dalam ../tests/test_module.pyatau ke dalam tests/test_module.py.


1

Dalam C #, saya biasanya memisahkan tes menjadi rakitan terpisah.

Dalam Python - sejauh ini - saya cenderung menulis doctests, di mana tesnya ada di docstring suatu fungsi, atau meletakkannya di if __name__ == "__main__"blok di bagian bawah modul.


0

Saat menulis paket yang disebut "foo", saya akan memasukkan tes unit ke dalam paket terpisah "foo_test". Modul dan sub paket kemudian akan memiliki nama yang sama dengan modul paket SUT. Misalnya, tes untuk modul foo.xy ditemukan di foo_test.xy File __init__.py dari setiap paket pengujian kemudian berisi suite AllTests yang mencakup semua suite tes dari paket tersebut. setuptools menyediakan cara mudah untuk menentukan paket pengujian utama, sehingga setelah "python setup.py mengembangkan" Anda bisa menggunakan "python setup.py test" atau "python setup.py test -s foo_test.x.SomeTestSuite" ke hanya suite tertentu.


0

Saya meletakkan tes saya di direktori yang sama dengan kode yang diuji (CUT); untuk foo.pytes akan masukfoo_ut.py atau serupa. (Saya mengubah proses penemuan tes untuk menemukan ini.)

Ini menempatkan tes tepat di samping kode dalam daftar direktori, membuatnya jelas bahwa tes ada di sana, dan membuat membuka tes semudah mungkin ketika mereka berada di file yang terpisah. (Untuk editor baris perintah, vim foo*dan ketika menggunakan browser sistem file grafis, cukup klik pada file CUT dan kemudian file tes yang berbatasan langsung.)

Seperti yang telah ditunjukkan orang lain , ini juga membuatnya lebih mudah untuk melakukan refactor dan mengekstrak kode untuk digunakan di tempat lain jika memang diperlukan.

Saya benar-benar tidak menyukai gagasan untuk melakukan tes di pohon direktori yang sama sekali berbeda; mengapa mempersulit pengembang untuk membuka tes saat mereka membuka file dengan CUT? Ini tidak seperti sebagian besar pengembang sangat suka menulis atau mengutak-atik tes yang mereka akan mengabaikan hambatan untuk melakukan itu, daripada menggunakan penghalang sebagai alasan. (Justru sebaliknya, dalam pengalaman saya; bahkan ketika Anda membuatnya semudah mungkin, saya tahu banyak pengembang yang tidak dapat diganggu untuk menulis tes.)


-2

Saya baru-baru ini mulai memprogram dengan Python, jadi saya belum benar-benar memiliki kesempatan untuk mengetahui praktik terbaik. Tapi, saya sudah menulis modul yang masuk dan menemukan semua tes dan menjalankannya.

Jadi saya punya:

aplikasi/
 appfile.py
uji/
 appfileTest.py

Saya harus melihat bagaimana kelanjutannya ketika saya maju ke proyek yang lebih besar.


4
Hai, camelCase sedikit aneh di dunia python.
Stuart Axon
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.