Bagaimana cara saya mengatur dan membongkar kelas pytest saya dengan tes?


104

Saya menggunakan selenium untuk pengujian ujung ke ujung dan saya tidak bisa mendapatkan cara menggunakan setup_classdan teardown_classmetode.

Saya perlu mengatur browser dalam setup_classmetode, kemudian melakukan banyak tes yang didefinisikan sebagai metode kelas dan akhirnya keluar dari teardown_classmetode browser .

Tapi secara logis sepertinya solusi yang buruk, karena sebenarnya tes saya tidak akan bekerja dengan kelas, tetapi dengan objek. Saya meneruskan selfparam di dalam setiap metode pengujian, jadi saya dapat mengakses vars objek:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

Dan bahkan sepertinya tidak benar membuat instance browser untuk kelas .. Itu harus dibuat untuk setiap objek secara terpisah, bukan?

Jadi, saya perlu menggunakan __init__dan __del__metode daripada setup_classdan teardown_class?

Jawaban:


94

Menurut Finalisasi Fixture / mengeksekusi kode pembongkaran , praktik terbaik saat ini untuk penyiapan dan pembongkaran adalah menggunakan yieldalih-alih return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Menjalankannya menghasilkan

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

Cara lain untuk menulis kode pembongkaran adalah dengan menerima requestobjek -context ke dalam fungsi fixture Anda dan memanggil request.addfinalizermetodenya dengan fungsi yang melakukan pembongkaran satu atau beberapa kali:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Jadi Anda menyalin ini ke setiap file pengujian yang Anda akan membutuhkan sumber daya?
Andy Hayden

@AndyHayden Bergantung pada bagaimana Anda menulis perlengkapan Anda, Anda dapat memasukkannya ke dalam setiap file tes di mana Anda membutuhkannya atau Anda dapat meletakkannya di file conftest.py stackoverflow.com/questions/34466027/…
Everett Toews

2
Namun ini bukan pengaturan kelas, bukan? Ini akan dieksekusi sebelum setiap metode pengujian di kelas.
malhar

1
Dalam kasus khusus ini, ini hanya dijalankan ketika digunakan sebagai param dalam metode pengujian. misalnya resourceparameter ditest_that_depends_on_resource(self, resource)
Everett Toews

65

Ketika Anda menulis "tes yang didefinisikan sebagai metode kelas" , apakah yang Anda maksud adalah metode kelas (metode yang menerima kelasnya sebagai parameter pertama) atau hanya metode biasa (metode yang menerima turunan sebagai parameter pertama)?

Karena contoh Anda digunakan selfuntuk metode pengujian, saya mengasumsikan yang terakhir, jadi Anda hanya perlu menggunakan setup_method:

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

Contoh metode pengujian diteruskan ke setup_methoddan teardown_method, tetapi dapat diabaikan jika kode penyiapan / pembongkaran Anda tidak perlu mengetahui konteks pengujian. Informasi lebih lanjut dapat ditemukan di sini .

Saya juga menyarankan agar Anda membiasakan diri dengan perlengkapan py.test , karena ini adalah konsep yang lebih kuat.


1
Fixtures lebih lemah daripada metode kelas: mereka tidak mengizinkan penghancuran objek yang tidak dibuat olehnya (yang seringkali memang diperlukan). Selain itu terima kasih informasinya.
wvxvw

Ini mengejutkan saya saat meningkatkan basis kode dari rilis pytest 3.0.x hingga varian 4.x. Beberapa kode lama yang digunakan setup_classdengan metode tiruan dan sejenisnya perlu dimodernisasi. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

28

Ini mungkin membantu http://docs.pytest.org/en/latest/xunit_setup.html

Dalam rangkaian pengujian saya, saya mengelompokkan kasus pengujian saya ke dalam beberapa kelas. Untuk penyiapan dan pembongkaran yang saya perlukan untuk semua kasus pengujian di kelas itu, saya menggunakan metode setup_class(cls)and teardown_class(cls)class.

Dan untuk penyiapan dan pembongkaran yang saya perlukan untuk setiap kasus uji, saya menggunakan setup_method(method)danteardown_method(methods)

Contoh:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Sekarang ketika saya menjalankan pengujian saya, saat eksekusi TestClass dimulai, ia mencatat detail kapan mulai dieksekusi, saat mengakhiri eksekusi dan sama untuk metode ..

Anda dapat menambahkan langkah penyiapan dan pembongkaran lain yang mungkin Anda miliki di lokasi masing-masing.

Semoga membantu!


Hai @Kiran, apa perbedaan antara setup_classvs setup_method?
imsrgadich

1
@imsrgadich Saat Anda mengatur kasus pengujian Anda ke dalam kelas, <setup / teardown> _class digunakan untuk langkah penyiapan dan pembongkaran kelas dan <setup / teardown> _method adalah langkah masing-masing untuk setiap metode kasus pengujian.
Kiran Vemuri

1
Sial ... sekarang aku mengerti! terjebak di atasnya selama beberapa jam. Jadi, untuk meletakkan segala sesuatunya dalam perspektif. Untuk <setup/teardown>_classseluruh kelas. Di sini, bisa berupa hal-hal seperti mengatur link ke DB atau memuat datafile. Dan kemudian, setiap kasus uji dapat memiliki penyiapannya sendiri dalam bentuk <setup/teardown>_method. Semuanya sudah jelas sekarang. Terima kasih banyak!
imsrgadich

24

Seperti yang disarankan oleh @Bruno, menggunakan perlengkapan pytest adalah solusi lain yang dapat diakses untuk kedua kelas pengujian atau bahkan hanya fungsi pengujian sederhana. Berikut ini contoh pengujian fungsi python2.7 :

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

Jadi, menjalankan test_1...menghasilkan:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Perhatikan yang stuff_i_setupdireferensikan di fixture, memungkinkan objek itu menjadi setupdan torn downuntuk pengujian berinteraksi dengannya. Anda bisa membayangkan ini bisa berguna untuk objek persisten, seperti database hipotetis atau beberapa koneksi, yang harus dibersihkan sebelum setiap pengujian dijalankan agar tetap terisolasi.


13

Kode Anda harus berfungsi seperti yang Anda harapkan jika Anda menambahkan @classmethoddekorator.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

Lihat http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/


Ini persis seperti yang muncul dalam dokumentasi. Masalah yang saya hadapi dengan dok adalah bahwa saya mengalami kesulitan memahami konteks: diri secara tradisional disebut sebagai diri, bukan cls, jadi ini tampak aneh bagi saya, di luar konteks kelas itu sendiri. Kiran (di atas) memberikan konteks ini.
Cognitiaclaeves

1
@Cognitiaclaeves "self secara tradisional disebut sebagai diri, bukan cls" Ya, selfdigunakan untuk metode contoh, di mana argumen pertama adalah contoh objek spesifik tempat operasi metode berlangsung, sementara clsdigunakan untuk @classmethods, yang terikat ke kelas dan bukan turunan dari kelas (yaitu objek).
code_dredd
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.