Làm cách nào để thiết lập chính xác và chia nhỏ lớp pytest của tôi với các bài kiểm tra?


100

Tôi đang sử dụng selen để thử nghiệm đầu cuối và tôi không thể biết cách sử dụng setup_classteardown_classphương pháp.

Tôi cần thiết lập trình duyệt trong setup_classphương thức, sau đó thực hiện một loạt các thử nghiệm được định nghĩa là phương thức lớp và cuối cùng thoát khỏi trình duyệt trong teardown_classphương thức.

Nhưng về mặt logic, nó có vẻ là một giải pháp tồi, bởi vì trên thực tế, các bài kiểm tra của tôi sẽ không hoạt động với lớp, mà với đối tượng. Tôi vượt qua selftham số bên trong mọi phương pháp thử nghiệm, vì vậy tôi có thể truy cập các vars của đối tượng:

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
    

Và có vẻ như không đúng khi tạo cá thể trình duyệt cho lớp .. Nó nên được tạo cho mọi đối tượng riêng biệt, phải không?

Vì vậy, tôi cần sử dụng __init____del__các phương pháp thay vì setup_classteardown_class?

Câu trả lời:


93

Theo hoàn thiện / thực thi mã xé nhỏ của Lịch thi đấu , phương pháp tốt nhất hiện tại để thiết lập và chia nhỏ là sử dụng yieldthay vì 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))

Chạy nó dẫn đến

$ 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 ===

Một cách khác để viết mã teardown là chấp nhận một requestđối tượng -context vào hàm fixture của bạn và gọi request.addfinalizerphương thức của nó với một hàm thực hiện teardown một hoặc nhiều lần:

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

Vì vậy, bạn sao chép nó vào mọi tệp thử nghiệm mà bạn sẽ cần tài nguyên?
Andy Hayden

@AndyHayden Tùy thuộc vào cách bạn viết đồ đạc của bạn, bạn có thể đặt nó vào mỗi tập tin kiểm tra nơi bạn cần hoặc bạn có thể đặt nó trong một tập tin conftest.py stackoverflow.com/questions/34466027/...
Everett Toews

2
Tuy nhiên, đây không phải là một thiết lập lớp, phải không? Nó sẽ thực thi trước mọi phương thức kiểm tra trong lớp.
malhar

1
Trong trường hợp cụ thể này, nó chỉ được thực thi khi được sử dụng làm tham số trong một phương pháp thử nghiệm. ví dụ: resourceparam intest_that_depends_on_resource(self, resource)
Everett Toews

64

Khi bạn viết "các bài kiểm tra được định nghĩa là phương thức lớp" , bạn thực sự muốn nói đến các phương thức lớp (các phương thức nhận lớp của nó làm tham số đầu tiên) hay chỉ là các phương thức thông thường (các phương thức nhận một thể hiện làm tham số đầu tiên)?

Vì ví dụ của bạn sử dụng selfcho các phương pháp thử nghiệm mà tôi đang giả định là phương pháp sau, vì vậy bạn chỉ cần sử dụng setup_methodthay thế:

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

Phiên bản phương pháp thử nghiệm được chuyển đến setup_methodteardown_method, nhưng có thể bị bỏ qua nếu mã thiết lập / xé nhỏ của bạn không cần biết bối cảnh thử nghiệm. Thông tin thêm có thể được tìm thấy ở đây .

Tôi cũng khuyên bạn nên tự làm quen với đồ đạc của py.test , vì chúng là một khái niệm mạnh mẽ hơn.


1
Fixtures yếu hơn các phương thức lớp: chúng không cho phép phá hủy các đối tượng không phải do chúng tạo ra (thường là những gì thực sự cần thiết). Ngoài ra, cảm ơn bạn đã thông tin.
wvxvw

Điều này đã xảy ra với tôi trong khi nâng cấp cơ sở mã từ bản phát hành 3.0.x của pytest lên biến thể 4.x. Một số mã cũ hơn được sử dụng setup_classvới các phương pháp giả mạo và những mã tương tự cần được hiện đại hóa. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

28

Điều này có thể hữu ích cho http://docs.pytest.org/en/latest/xunit_setup.html

Trong bộ thử nghiệm của mình, tôi nhóm các trường hợp thử nghiệm của mình thành các lớp. Để thiết lập và chia nhỏ, tôi cần cho tất cả các trường hợp thử nghiệm trong lớp đó, tôi sử dụng setup_class(cls)và các phương thức phân loại teardown_class(cls).

Và để thiết lập và chia nhỏ tôi cần cho mỗi trường hợp thử nghiệm, tôi sử dụng setup_method(method)teardown_method(methods)

Thí dụ:

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

Bây giờ khi tôi chạy các bài kiểm tra của mình, khi quá trình thực thi TestClass bắt đầu, nó ghi lại các chi tiết về thời điểm bắt đầu thực thi, khi nào nó kết thúc thực thi và tương tự đối với các phương thức ..

Bạn có thể thêm các bước thiết lập và xé nhỏ khác mà bạn có thể có ở các vị trí tương ứng.

Hy vọng nó giúp!


Xin chào @Kiran, sự khác biệt giữa cái setup_classvs là setup_methodgì?
imsrgadich 10/09/19

1
@imsrgadich Khi bạn tổ chức các trường hợp thử nghiệm của mình thành các lớp, <setup / teardown> _class được sử dụng cho các bước thiết lập và chia nhỏ của lớp và <setup / teardown> _method là các bước tương ứng cho mỗi phương pháp trường hợp thử nghiệm.
Kiran Vemuri

1
Chết tiệt ... bây giờ tôi hiểu rồi! đã bị mắc kẹt trên đó trong vài giờ. Vì vậy, để đặt mọi thứ trong quan điểm. Các <setup/teardown>_classcho cả lớp. Ở đây, có thể là những thứ như thiết lập liên kết tới DB hoặc tải tệp dữ liệu. Và sau đó, mỗi trường hợp thử nghiệm có thể có thiết lập riêng của chúng ở dạng <setup/teardown>_method. Mọi thứ đã rõ ràng hơn nhiều. Cảm ơn rất nhiều!
imsrgadich 12/09/19

24

Như @Bruno đã đề xuất, sử dụng đồ đạc pytest là một giải pháp khác có thể truy cập được cho cả các lớp thử nghiệm hoặc thậm chí chỉ là các chức năng thử nghiệm đơn giản. Đây là một ví dụ kiểm tra các chức năng của 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..."

Vì vậy, chạy test_1...tạo ra:

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

Lưu ý rằng nó stuff_i_setupđược tham chiếu trong vật cố định, cho phép đối tượng đó được setuptorn downđối với thử nghiệm mà nó tương tác. Bạn có thể tưởng tượng điều này có thể hữu ích cho một đối tượng liên tục, chẳng hạn như cơ sở dữ liệu giả định hoặc một số kết nối, phải được xóa trước mỗi lần chạy thử nghiệm để giữ chúng cách ly.


13

Mã của bạn sẽ hoạt động như bạn mong đợi nếu bạn thêm trình @classmethodtrang trí.

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

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

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


Đây là khá chính xác những gì xuất hiện trong tài liệu. Rắc rối mà tôi gặp phải với tài liệu là tôi gặp khó khăn trong việc hiểu ngữ cảnh: self thường được gọi là self, chứ không phải cls, vì vậy điều này có vẻ kỳ lạ đối với tôi, ngoài ngữ cảnh của chính class. Kiran (ở trên) cung cấp bối cảnh này.
Cognitiaclaeves

1
@Cognitiaclaeves "self theo truyền thống được gọi là self, not cls" Có, selfđược sử dụng cho các phương thức phiên bản, trong đó đối số đầu tiên là cá thể đối tượng cụ thể mà hoạt động của phương thức đang diễn ra, trong khi clsđược sử dụng cho @classmethods, được liên kết với lớp chứ không phải một thể hiện của lớp (tức là một đối tượng).
code_dredd
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.