ฉันจะตั้งค่าและแยกย่อยสำหรับคลาส pytest ด้วยการทดสอบอย่างถูกต้องได้อย่างไร


117

ฉันใช้ซีลีเนียมในการทดสอบตั้งแต่ต้นจนจบและไม่สามารถหาวิธีใช้setup_classและteardown_classวิธีการได้

ฉันต้องตั้งค่าเบราว์เซอร์ในsetup_classวิธีการจากนั้นทำการทดสอบหลายชุดที่กำหนดเป็นวิธีการเรียนและในที่สุดก็ออกจากเบราว์เซอร์ในteardown_classวิธี

แต่ในทางเหตุผลดูเหมือนว่าจะเป็นวิธีแก้ปัญหาที่ไม่ดีเพราะอันที่จริงการทดสอบของฉันจะใช้ไม่ได้กับคลาส แต่ใช้กับวัตถุ ฉันส่งselfพารามิเตอร์ในทุกวิธีการทดสอบดังนั้นฉันจึงสามารถเข้าถึงตัวแปรของวัตถุ:

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
    

และดูเหมือนว่าจะไม่ถูกต้องในการสร้างอินสแตนซ์เบราว์เซอร์สำหรับคลาส .. มันควรจะสร้างสำหรับทุกออบเจ็กต์แยกกันใช่ไหม?

ดังนั้นฉันต้องใช้__init__และ__del__วิธีการแทนsetup_classและteardown_class?

คำตอบ:


105

ตามการสรุปผลของ Fixture / การเรียกใช้โค้ด teardownแนวทางปฏิบัติที่ดีที่สุดในปัจจุบันสำหรับการตั้งค่าและการฉีกขาดคือการใช้yieldแทน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))

การรันจะส่งผลให้

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

อีกวิธีหนึ่งในการเขียนโค้ด teardown คือการรับrequestอ็อบเจกต์ -contextในฟังก์ชันฟิกซ์เจอร์ของคุณและเรียกrequest.addfinalizerใช้เมธอดของมันด้วยฟังก์ชันที่ดำเนินการฉีกขาดหนึ่งหรือหลายครั้ง

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

คุณจึงคัดลอกสิ่งนี้ลงในไฟล์ทดสอบทุกไฟล์ที่คุณต้องการทรัพยากรหรือไม่?
Andy Hayden

@AndyHayden ขึ้นอยู่กับว่าคุณเขียนส่วนควบของคุณอย่างไรคุณสามารถใส่ลงในไฟล์ทดสอบทุกไฟล์ที่คุณต้องการหรือใส่ไว้ในไฟล์ conftest.py stackoverflow.com/questions/34466027/…
Everett Toews

2
แต่นี่ไม่ใช่การตั้งค่าคลาสใช่ไหม? มันจะดำเนินการก่อนทุกวิธีการทดสอบในชั้นเรียน
malhar

1
ในกรณีนี้จะดำเนินการเมื่อใช้เป็นพารามิเตอร์ในวิธีทดสอบเท่านั้น เช่นresourceพารามิเตอร์ในtest_that_depends_on_resource(self, resource)
Everett Toews

1
โปรดทราบว่าคุณสามารถตั้งค่าขอบเขตฟิกซ์เจอร์เป็น 'class' และ autouse เป็น true เพื่อให้แน่ใจว่ามีการเรียกโค้ดหนึ่งครั้งสำหรับแต่ละคลาสโดยไม่ต้องรวมเป็นพารามิเตอร์ในการเรียกทดสอบใด ๆ : `` pytest.fixture (scope = " class ", autouse = True) def resource (): print (" setup ") yield" resource "print (" teardown ")` `
Chris

69

เมื่อคุณเขียน"การทดสอบกำหนดเป็นวิธีการเรียน"คุณหมายความว่าจริงๆวิธีการเรียน (วิธีการที่ได้รับของชั้นเป็นพารามิเตอร์แรก) หรือเพียงแค่วิธีการปกติ (วิธีการที่ได้รับเช่นเป็นพารามิเตอร์แรก)?

เนื่องจากตัวอย่างของคุณใช้selfสำหรับวิธีการทดสอบฉันจึงคิดอย่างหลังดังนั้นคุณจึงต้องใช้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

อินสแตนซ์วิธีการทดสอบจะถูกส่งผ่านไปsetup_methodและteardown_methodแต่สามารถละเว้นได้หากโค้ดการตั้งค่า / การฉีกขาดของคุณไม่จำเป็นต้องทราบบริบทการทดสอบ ข้อมูลเพิ่มเติมสามารถพบได้ที่นี่

ฉันขอแนะนำให้คุณทำความคุ้นเคยกับการติดตั้งของ py.test เนื่องจากเป็นแนวคิดที่ทรงพลังกว่า


1
การแข่งขันนั้นอ่อนกว่าวิธีคลาส: ไม่อนุญาตให้ทำลายวัตถุที่ไม่ได้สร้างขึ้น (ซึ่งมักเป็นสิ่งที่จำเป็นจริงๆ) นอกเหนือจากนั้นขอขอบคุณสำหรับข้อมูล
wvxvw

สิ่งนี้โดนใจฉันในขณะที่อัปเกรด codebase จาก pytest รุ่น 3.0.x เป็นตัวแปร 4.x โค้ดเก่า ๆ บางตัวที่ใช้setup_classกับวิธีการเยาะเย้ยและสิ่งที่คล้ายกันนั้นจำเป็นต้องทำให้ทันสมัย setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

31

ซึ่งอาจช่วยได้http://docs.pytest.org/en/latest/xunit_setup.html

ในชุดทดสอบของฉันฉันจัดกลุ่มกรณีทดสอบเป็นชั้นเรียน สำหรับการตั้งค่าและการแยกย่อยฉันต้องการสำหรับกรณีทดสอบทั้งหมดในคลาสนั้นฉันใช้setup_class(cls)และteardown_class(cls)วิธีการเรียน

และสำหรับการตั้งค่าและการแยกส่วนที่ฉันต้องการสำหรับแต่ละกรณีทดสอบฉันใช้setup_method(method)และteardown_method(methods)

ตัวอย่าง:

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

ตอนนี้เมื่อฉันเรียกใช้การทดสอบของฉันเมื่อการดำเนินการ TestClass เริ่มต้นระบบจะบันทึกรายละเอียดเมื่อเริ่มต้นการดำเนินการเมื่อสิ้นสุดการดำเนินการและเหมือนกันสำหรับวิธีการ ..

คุณสามารถเพิ่มขั้นตอนการตั้งค่าและขั้นตอนการแยกย่อยอื่น ๆ ที่คุณอาจมีในสถานที่นั้น ๆ

หวังว่าจะช่วยได้!


สวัสดี @Kiran ความแตกต่างระหว่างsetup_classvs setup_methodคืออะไร?
imsrgadich

1
@imsrgadich เมื่อคุณจัดระเบียบกรณีทดสอบของคุณเป็นชั้นเรียน <setup / teardown> _class จะใช้สำหรับขั้นตอนการตั้งค่าและการแยกย่อยของคลาสและ <setup / teardown> _method เป็นขั้นตอนตามลำดับสำหรับแต่ละวิธีกรณีทดสอบ
Kiran Vemuri

1
ไอ้ ... ตอนนี้ฉันเข้าใจแล้ว! ติดอยู่ไม่กี่ชั่วโมง ดังนั้นเพื่อให้สิ่งต่าง ๆ ในมุมมอง <setup/teardown>_classสำหรับทั้งชั้น ที่นี่อาจเป็นสิ่งต่างๆเช่นการตั้งค่าลิงก์ไปยังฐานข้อมูลหรือการโหลดดาต้าไฟล์ จากนั้นแต่ละกรณีการทดสอบสามารถมีการตั้งค่าของตนเองในรูปแบบ<setup/teardown>_method. ตอนนี้ทุกอย่างชัดเจนมาก ขอบคุณมาก!
imsrgadich

25

ตามที่ @Bruno แนะนำการใช้ pytest fixtures เป็นอีกวิธีหนึ่งที่สามารถเข้าถึงได้สำหรับทั้งคลาสทดสอบหรือแม้แต่ฟังก์ชั่นทดสอบง่ายๆ นี่คือตัวอย่างการทดสอบฟังก์ชัน 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..."

ดังนั้นการทำงานจึงtest_1...ก่อให้เกิด:

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

สังเกตว่าstuff_i_setupมีการอ้างถึงในฟิกซ์เจอร์ซึ่งอนุญาตให้วัตถุนั้นเป็นsetupและtorn downสำหรับการทดสอบที่โต้ตอบด้วย คุณสามารถจินตนาการได้ว่าสิ่งนี้อาจมีประโยชน์สำหรับวัตถุที่ยังคงอยู่เช่นฐานข้อมูลสมมุติฐานหรือการเชื่อมต่อบางอย่างซึ่งต้องถูกล้างก่อนการทดสอบแต่ละครั้งจะดำเนินการเพื่อให้แยกออกจากกัน


14

รหัสของคุณควรทำงานได้ตามที่คุณคาดไว้หากคุณเพิ่ม@classmethodมัณฑนากร

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

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

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


นี่เป็นสิ่งที่ปรากฏในเอกสาร ปัญหาที่ฉันมีกับเอกสารคือฉันมีปัญหาในการทำความเข้าใจบริบท: self ถูกเรียกแบบดั้งเดิมว่า self ไม่ใช่ cls ดังนั้นสิ่งนี้จึงดูแปลกสำหรับฉันจากบริบทของชั้นเรียนเอง Kiran (ด้านบน) ให้บริบทนี้
Cognitiaclaeves

1
@Cognitiaclaeves "self เรียกตามเนื้อผ้าว่า self ไม่ใช่ cls"ใช่selfใช้สำหรับวิธีการเช่นโดยที่อาร์กิวเมนต์แรกคืออินสแตนซ์วัตถุเฉพาะที่การดำเนินการของวิธีการเกิดขึ้นในขณะที่clsใช้สำหรับ@classmethods ซึ่งถูกผูกไว้กับ คลาสและไม่ใช่อินสแตนซ์ของคลาส (เช่นอ็อบเจ็กต์)
code_dredd

1
import pytest
class Test:
    @pytest.fixture()
    def setUp(self):
        print("setup")
        yield "resource"
        print("teardown")

    def test_that_depends_on_resource(self, setUp):
        print("testing {}".format(setUp))

ในการเรียกใช้:

pytest nam_of_the_module.py -v 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.