Pytest-django: Bagaimana cara menggunakan tanda Django_db pada perlengkapan sesi?

Dibuat pada 14 Sep 2017  ·  15Komentar  ·  Sumber: pytest-dev/pytest-django

Saya memiliki situasi di mana saya perlu memuat perlengkapan db besar yang dibuat oleh suatu fungsi. Perlengkapan diperlukan di semua tes api. Jadi saya membuat perlengkapan sesi di conftest.py yang akan melakukannya. Tetapi masalahnya adalah pytest melempar pengecualian berikut meskipun saya telah menandai django_db :

E Failed: Database access not allowed, use the "django_db" mark to enable it.
Di bawah ini adalah cuplikan kode saya.

from permission.helpers import update_permissions

pytestmark = [
        pytest.mark.django_db(transaction = True),]

@pytest.fixture(scope="session", autouse = True)
def permission(request):
        load_time_consuming_db_fixture()
db-configuration enhancement

Komentar yang paling membantu

Ini telah menjadi titik sakit terbesar bagi saya yang datang dari tes berbasis kelas UnitTest Django, ke pytest-Django -- di Django kami menggunakan setUpTestData untuk menjalankan operasi DB yang mahal sekali (setara dengan perlengkapan pytest cakupan sesi), dan kemudian ada trik licik untuk menjalankan obj.refresh_from_db() di setUp untuk menyegarkan referensi kelas.

Bahkan jika saya hanya membuat satu contoh model DB, dan memuat ulang setiap TC, ini hampir selalu lebih cepat daripada membuat di setiap kasus uji.

Akan sangat bagus jika kita bisa mendapatkan perlengkapan dengan cakupan sesi dari pytest-tipsi-Django yang digabungkan ke hulu, jika itu memungkinkan; butuh sedikit penggalian untuk menemukan masalah dan solusi ini.

Semua 15 komentar

@ludbek kami juga melewatkan fitur tersebut dan membuat plugin untuk fitur ini:
https://github.com/tipsi/pytest-tipsi-django (penggunaan: https://github.com/tipsi/pytest-tipsi-django/blob/master/test_django_plugin/app/tests/test_transactions.py)
Setara dengan:
https://github.com/tipsi/pytest-tipsi-testing
Ini memberi Anda kemampuan untuk membuat sarang transaksi dan urutan eksekusi/shutdown yang benar.

@cybergrind Terima kasih telah membalas. Saya pasti akan memeriksanya dan memberi tahu Anda bagaimana hasilnya.

Ini telah menjadi titik sakit terbesar bagi saya yang datang dari tes berbasis kelas UnitTest Django, ke pytest-Django -- di Django kami menggunakan setUpTestData untuk menjalankan operasi DB yang mahal sekali (setara dengan perlengkapan pytest cakupan sesi), dan kemudian ada trik licik untuk menjalankan obj.refresh_from_db() di setUp untuk menyegarkan referensi kelas.

Bahkan jika saya hanya membuat satu contoh model DB, dan memuat ulang setiap TC, ini hampir selalu lebih cepat daripada membuat di setiap kasus uji.

Akan sangat bagus jika kita bisa mendapatkan perlengkapan dengan cakupan sesi dari pytest-tipsi-Django yang digabungkan ke hulu, jika itu memungkinkan; butuh sedikit penggalian untuk menemukan masalah dan solusi ini.

hai @paultiplady

Saya tidak yakin bahwa pendekatan dari pytest-tipsi-djanjo cocok dengan model pengujian biasa untuk pytest . Perbedaan yang paling mencolok adalah penyelesaian perlengkapan: saat ini pytest tidak secara eksplisit menyelesaikan perlengkapan yang tidak perlu dengan cakupan yang lebih luas. Jadi Anda perlu secara eksplisit menyelesaikan transaksi dalam urutan tertentu dan secara umum, ini dapat menyebabkan efek yang sangat berbeda (saat ini pytest dapat membuat perlengkapan tetap aktif bahkan jika pengujian aktif dan perlengkapannya tidak memerlukannya sama sekali).

Kami harus mengubah pengujian dalam proyek kami ke pendekatan ini karena terkadang kami perlu menguji beberapa skenario besar dan kami telah mengganti manajemen transaksi manual yang ada dalam pengujian besar dengan perlengkapan yang sedikit lebih baik, tetapi masih memerlukan perhatian pada pengujian pesanan.

Saat ini saya hanya dapat melihat satu solusi untuk itu: memasukkan semacam FAQ ke dalam dokumentasi.

Terima kasih atas detail tambahannya @cybergrind. Saya telah menggali ini sedikit lebih banyak, tetapi telah kehabisan waktu untuk hari ini -- di sinilah saya harus melakukannya, saya akan menghargai pemeriksaan kewarasan apakah pendekatan ini berguna atau tidak, karena saya tidak begitu akrab dengan internal pytest.

Saya juga tidak mengerti apa yang Anda maksud dengan "pytest tidak secara eksplisit menyelesaikan perlengkapan yang tidak perlu dengan cakupan yang lebih luas", bisakah Anda memperluasnya sedikit lebih banyak? Apakah itu mengacu pada finalis? Itu mungkin mempengaruhi apa yang saya tulis di bawah ini.

Plugin pytest-django menggunakan tanda django_db , yang ditangani di _django_db_marker di plugin.py (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py #L375), memanggil perlengkapan db cakupan-fungsi (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixtures.py#L142). Perlengkapan ini membuat instance Django TestCase, dan memanggil fungsi _pre_setup -nya (dan mengantrekan _post_teardown ).

Saya dapat melihat beberapa opsi:

Saya ingin tahu apakah kami dapat memperluas _django_db_marker untuk melakukan kelas- atau sesi- secara opsional
pengaturan scoped juga, yang pada dasarnya akan melakukan hal yang sama dengan db , tetapi memanggil yang setara dengan cls.setUpTestData atau fungsi yang diteruskan dalam tanda kwargs sebagai gantinya.

Untuk cakupan kelas, dan saya berasumsi untuk cakupan sesi juga, perilaku yang diharapkan adalah memutar kembali DB setelahnya, jadi pada dasarnya kami membutuhkan perlengkapan cakupan yang memicu dalam urutan yang benar, dan masing-masing menyiapkan atom mereka sendiri transaksi. Saya percaya itu berarti ini perlu modifikasi pada perlengkapan db , daripada perlengkapan terpisah yang berjalan di sampingnya.

Dengan cara itu kami akan dengan benar memicu pengaturan tingkat kelas/sesi apa pun yang ditentukan, dan pengaturan itu akan dipanggil sekali per kelas/sesi. Saya percaya modifikasi pada tanda itu sendiri diperlukan karena jika Anda baru saja mengatur fungsi cakupan sesi yang secara manual memicu django_db_blocker.unblock() , itu tampaknya terjadi setelah tanda Django_db telah mengatur transaksi pertama.

Ini mungkin terlihat seperti ini (di plugin.py _django_db_marker() ):

    if marker:
        if marker.session_db:
            getfixturevalue(request, 'db_session')

        if marker.class_db:
            getfixturevalue(request, 'db_class')

        validate_django_db(marker)
        if marker.transaction:
            getfixturevalue(request, 'transactional_db')
        else:
            getfixturevalue(request, 'db')

Apakah ini pembicaraan gila, atau apakah utas ini perlu ditelusuri lebih lanjut?

Mengenai finalisasi: https://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py

Tes ini tidak bekerja tanpa finalisasi eksplisit, sama seperti perlengkapan database tingkat non-fungsi. Dan ini tentang implementasi pytest, jadi tidak ada yang bisa dilakukan di pytest-Django untuk memperbaikinya.

Duplikat #388 dan #33

Terima kasih, penutup.

33 ditutup, dan #388 tidak berisi diskusi yang berarti (tidak seperti yang ini). Tampaknya aneh untuk menutup yang ini @blueyed , jika ada yang saya sarankan untuk menutup #388 dan menjadikan yang ini tiket kanonik untuk masalah ini.

👍 terima kasih!

Saya juga akan sangat menghargai fungsi ini.

@mkokotovich
Berapa banyak? Cukup untuk mewujudkannya sendiri? ;)

Bagaimanapun, masalah utama / mendasar di sini (dari komentar asli) adalah bahwa DB disetel ulang selama pengujian, jadi tidak ada cara sepele untuk memiliki perlengkapan lingkup sesi seperti itu.

Apa yang mungkin berhasil adalah sesuatu bersama:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        with transaction.atomic():  # XXX: could/should use `TestCase_enter_atomics` for multiple dbs
            load_time_consuming_db_fixture()
            yield

Idenya adalah untuk membungkus semuanya menjadi blok atom tambahan. Namun ini belum diuji, dan Anda mungkin perlu menggunakan TransactionTestCase sebenarnya untuk ini.

@paultiplady
Komentar Anda terdengar bagus - yaitu layak untuk ditelusuri lebih lanjut AFAICS (lihat juga komentar saya sebelumnya).

@blueyed kami menggunakan pendekatan seperti itu selama lebih dari satu tahun (bungkus semuanya menjadi blok atom tambahan) ini berfungsi dengan cukup baik.
Tetapi Anda tidak bisa begitu saja memasukkan sesuatu seperti itu karena pytest tidak memiliki deterministik di mana tingkat sesi (dan lainnya lebih tinggi dari pengujian) akan ditutup sehingga akan memerlukan pelacakan dependensi transaksi db terlepas dari mereka saling membutuhkan atau tidak secara langsung .
Jadi, Anda perlu secara eksplisit melacak tumpukan transaksi dan menutup transaksi bersarang sebelum pengujian berikutnya. Dalam dapat dilakukan sedemikian rupa: https://github.com/tipsi/pytest-tipsi-django/blob/master/pytest_tipsi_django/django_fixtures.py#L46

maaf saya ingin mengklarifikasi karena saya yakin saya mengalami masalah yang sama:

Jika saya ingin membuat perlengkapan yang bergantung pada pembuatan objek db baru, saya pikir saya bisa melakukannya

@pytest.mark.django_db(transaction=True)
@pytest.fixture(scope="session")
def object():
    object = Object.create_object(params)
    yield object
    // or alternatively
    object = mixer.blend('myObject')
    yield object

Namun, saya menerima kesalahan berikut saat menjalankan uji kasus: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat