วิธีกระจายการทดสอบหน่วย django ผ่านไฟล์หลายไฟล์


126
  • ฉันมีแอปพลิเคชัน python-django
  • ฉันกำลังใช้กรอบการทดสอบหน่วย
  • การทดสอบจะจัดเรียงในไฟล์ "tests.py" ในไดเร็กทอรีโมดูล
  • ฉันกำลังทำการทดสอบผ่าน ./manage.py test app

ตอนนี้ ..

  • tests.pyไฟล์จะได้รับค่อนข้างใหญ่ / ซับซ้อน / ยุ่ง
  • ฉันต้องการแยกtests.pyออกเป็นชุดการทดสอบย่อย ๆ ...

อย่างไร?

คำตอบ:


47

พฤติกรรมเปลี่ยนไปใน Django 1.6 ดังนั้นจึงไม่จำเป็นต้องสร้างแพ็คเกจอีกต่อไป test*.pyเพียงแค่ชื่อไฟล์ของคุณ

จากเอกสาร Django 1.7

เมื่อคุณเรียกใช้การทดสอบของคุณลักษณะการทำงานเริ่มต้นของยูทิลิตี้ทดสอบคือการค้นหากรณีทดสอบทั้งหมด (นั่นคือคลาสย่อยของ unittest.TestCase) ในไฟล์ใด ๆ ที่มีชื่อขึ้นต้นด้วยการทดสอบสร้างชุดทดสอบโดยอัตโนมัติจากกรณีทดสอบเหล่านั้น และเรียกใช้ชุดโปรแกรมนั้น

จากDjango เอกสาร 1.6 ,

การค้นพบการทดสอบขึ้นอยู่กับการค้นพบการทดสอบในตัวของโมดูลที่ไม่เหมาะสมที่สุด ตามค่าเริ่มต้นสิ่งนี้จะค้นพบการทดสอบในไฟล์ชื่อ "test * .py" ภายใต้ไดเร็กทอรีการทำงานปัจจุบัน

พฤติกรรมก่อนหน้าจากเอกสาร Django 1.5 :

เมื่อคุณเรียกใช้การทดสอบของคุณลักษณะการทำงานเริ่มต้นของยูทิลิตี้ทดสอบคือการค้นหากรณีทดสอบทั้งหมด (นั่นคือคลาสย่อยของ unittest.TestCase) ใน models.py และ tests.py สร้างชุดทดสอบโดยอัตโนมัติจากกรณีทดสอบเหล่านั้น และเรียกใช้ชุดโปรแกรมนั้น

มีวิธีที่สองในการกำหนดชุดทดสอบสำหรับโมดูล: ถ้าคุณกำหนดฟังก์ชันที่เรียกว่า suite () ใน models.py หรือ tests.py นักวิ่งทดสอบ Django จะใช้ฟังก์ชันนั้นเพื่อสร้างชุดทดสอบสำหรับโมดูลนั้น สิ่งนี้เป็นไปตามองค์กรที่แนะนำสำหรับการทดสอบหน่วย ดูเอกสาร Python สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีสร้างชุดทดสอบที่ซับซ้อน


4
ใน django 2.6 มันไม่ได้ค้นพบอะไรเลยจริงๆ…
LtWorf

2
ขณะนี้ใช้ Django 1.10 ฉันต้องการใส่test*.pyไฟล์ทั้งหมดของฉันในโฟลเดอร์ที่เรียกว่าtestsเพื่อให้โฟลเดอร์สะอาดซึ่งเป็นไปได้ แต่คุณต้องเรียกใช้./manage.py test app.testsและการนำเข้าแบบสัมพัทธ์ทั้งหมดต้องเพิ่มขึ้นในระดับหนึ่ง ( from .modelsกลายเป็นfrom ..models)
Scott Stevens

123

โปรดทราบว่าวิธีนี้ใช้ไม่ได้กับ Django 1.6 อีกต่อไปโปรดดูโพสต์นี้

คุณสามารถสร้างtestsโฟลเดอร์ด้วย___init___.pyภายใน (เพื่อให้กลายเป็นแพ็คเกจ) จากนั้นคุณเพิ่มไฟล์. py การทดสอบแยกที่นั่นและนำเข้าทั้งหมดใน___init___.py.

เช่น: แทนที่ไฟล์ test.pyไฟล์ด้วยโมดูลที่มีลักษณะและทำหน้าที่เหมือนไฟล์:

สร้างtestsไดเรกทอรีภายใต้แอปที่เป็นปัญหา

แอป
แอปพลิเค \ models.py
แอปพลิเค \ views.py
การทดสอบแอพพลิเค \
แอปพลิเค \ ทดสอบ \ __ init__.py
แอปพลิเค \ ทดสอบ \ bananas.py
แอปพลิเค \ ทดสอบ \ apples.py

นำเข้าโมดูลย่อยไปที่app\tests\__init__.py:

from bananas import *
from apples import *

ตอนนี้คุณสามารถใช้. /manage.py เหมือนกับว่าทั้งหมดอยู่ในไฟล์เดียว:

./manage.py test app.some_test_in_bananas

1
Doh คุณหมายถึงสร้างโมดูล 'การทดสอบ' ภายใต้แอปพลิเคชันที่ฉันกำลังทดสอบ ไม่ใช่แอปพลิเคชันใหม่ที่เรียกว่าการทดสอบ ฉันเข้าใจแล้ว. น่ากลัว ขอบคุณ!
John Mee

@ จอห์น: ฉันจำคำตอบไม่ได้อีกแล้ว! :-) แต่คุณพูดถูกอย่างยิ่งว่ามันคลุมเครือเกินไปแม้ว่าจะถูกต้องก็ตาม - ตัวอย่างของคุณทำให้ชัดเจนขัดกับถ้อยคำดั้งเดิมของฉัน
Tomasz Zieliński

2
@Tomasz .. คำพูดของคุณยังอยู่ - เหมือนเดิมทั้งหมด ฉันเพิ่งทำมันออกมาเล็กน้อยเนื่องจากคุณทำให้ฉันมาถูกทาง
John Mee

@ จอห์น: ฉันไม่โกรธเลยถ้านั่นคือสิ่งที่คุณหมายถึง :) มันเป็นเรื่องตลกที่เห็นคำตอบของตัวเองในรูปแบบที่แตกต่างไปเล็กน้อย
Tomasz Zieliński

4
@jMyles ถ้าโดย "นักวิ่งทดสอบ django ปกติ" คุณหมายความว่าpython manage.py test myappในความเป็นจริงคำตอบนี้ใช้ได้ดี (เพิ่งทดลองใช้)
Kirk Woll

27

คำตอบที่ระบุโดย Tomasz นั้นถูกต้อง อย่างไรก็ตามอาจเป็นเรื่องน่าเบื่อที่จะต้องแน่ใจว่าการนำเข้า__init__.pyตรงกับโครงสร้างไฟล์ของคุณ

หากต้องการตรวจหาการทดสอบทั้งหมดในโฟลเดอร์โดยอัตโนมัติคุณสามารถเพิ่มสิ่งนี้ได้ใน__init__.py:

import unittest

def suite():   
    return unittest.TestLoader().discover("appname.tests", pattern="*.py")

วิธีนี้จะช่วยให้คุณสามารถรันได้./manage.py test appnameแต่จะไม่จัดการกับการทดสอบเฉพาะ ในการทำเช่นนั้นคุณสามารถใช้รหัสนี้ (เช่นกันใน__init__.py):

import pkgutil
import unittest

for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
    module = loader.find_module(module_name).load_module(module_name)
    for name in dir(module):
        obj = getattr(module, name)
        if isinstance(obj, type) and issubclass(obj, unittest.case.TestCase):
            exec ('%s = obj' % obj.__name__)

ตอนนี้คุณสามารถเรียกใช้การทดสอบทั้งหมดของคุณผ่านmanage.py test appทางไฟล์manage.py test app.TestApples


คุณวางชิ้นที่สองไว้ที่ไหน?
rh0dium

ทั้งสองชิ้นเข้าสู่__init__.py
Bryce Drennan

โปรดทราบว่าถ้ามีชื่อแพคเกจการทดสอบของคุณตรงกับชื่อโมดูลระดับบนสุดที่ได้รับนำเข้าในระหว่างการดำเนินการทดสอบข้อมูลโค้ด pkgutil sys.modules[packagename]จะทำให้เกิดการนำเข้าที่จะล้มเหลวเพราะการทดสอบได้รับการบันทึกเป็น วิธีแก้ปัญหาอย่างรวดเร็วคือdelสิ่งที่ทำให้เกิดปัญหาตามมาข้างต้น (หรือคุณสามารถเปลี่ยนชื่อโฟลเดอร์ก็ได้))
Paul Fenney

นี่เป็นสิ่งที่ดี แต่ฉันพบข้อผิดพลาดที่เมื่อเรียกใช้การทดสอบระดับแอป ( python manage.py test appName) บิตรหัสที่สองจะทำให้เกิดข้อผิดพลาดที่ระบุว่า__path__ไม่พร้อมใช้งาน ฉันหลีกเลี่ยงมันโดยการห่อตัวอย่างที่สองในif '__path__' in locals():เช็คซึ่งเป็นเคล็ดลับ ขอบคุณสำหรับคำตอบ!
alukach

1
+1 สิ่งนี้ยังช่วยให้มั่นใจได้ว่าไฟล์init เป็นไปตามมาตรฐานการเข้ารหัสทั่วไปกล่าวคือไม่มี * หรือการนำเข้าที่ไม่ได้ใช้
Martin B.

13

เพียงสร้างโครงสร้างไดเรกทอรีของคุณดังนี้:

myapp/
    __init__.py
    tests/
        __init__.py
        test_one.py
        test_two.py
        ...
    ...

และpython manage.py test myappจะทำงานตามที่คาดหวัง.


5

http://docs.python.org/library/unittest.html#organizing-testsพูดถึงการแยกไฟล์ออกเป็นโมดูลและส่วนด้านบนจะมีตัวอย่าง


2
'บิตพิเศษ' ที่ฉันกำลังมองหามากกว่า rtfm คือสภาพแวดล้อมการตั้งค่า django ฐานข้อมูลและส่วนควบสำหรับการทดสอบ
John Mee

2

ไม่จำเป็นต้องเขียนโค้ดอะไรในการเริ่มต้น เพียงสร้างไดเรกทอรีย่อยในแอปของคุณ ข้อกำหนดเพียงอย่างเดียวคือไม่เรียกว่าการทดสอบ * สำหรับตัวอย่าง

app/
app/__init_.py
app/serializers.py
app/testing/
app/testing/__init__.py
app/testing/tests_serializers.py

1
ทำไมคุณไม่เรียกสิ่งนี้ว่าสิ่งที่เริ่มต้นด้วย "การทดสอบ"?
Serp C

ฉันใช้คำตอบนี้กับ Django 1.11.4 เหตุผลในการใช้งาน: (1) ไฟล์ "app / testing / __ init__.py" ยังคงว่างเปล่าและ (2) คำสั่งยังคงเป็น "แอปทดสอบ python Manage.py" พื้นฐาน
rprasad

2

ด้วย Django 2.2 วิธีแก้ปัญหาที่ง่ายและค่อนข้างดีคือการสร้างtestโฟลเดอร์ภายในแอพและคุณสามารถใส่test_...pyไฟล์ที่เกี่ยวข้องลงไปเพียงแค่เพิ่มลง__init__.pyในtestโฟลเดอร์


1

หากคุณมีการตั้งค่าที่ซับซ้อนมากขึ้นหรือไม่ต้องการใช้from ... import *คำสั่ง -type คุณสามารถกำหนดฟังก์ชันที่เรียกว่าsuiteใน tests.py (หรือการทดสอบ / __ init__.py) ซึ่งส่งคืนอินสแตนซ์ของunittest.TestSuite.


0

ฉันคิดว่า./manage.py testแค่ใช้เคล็ดลับการทดสอบทั้งหมด (ใน django> = 1.7)

หากการทดสอบการจัดระเบียบของคุณเกี่ยวกับการจัดกลุ่มและการจับเชอร์รี่และคุณชอบnoseใช้django nose :

python manage.py test another.test:TestCase.test_method

หากคุณรู้จักจมูกคุณก็จะรู้วิธี "สัญลักษณ์แทน" ได้ดีกว่าไฟล์ทั้งหมดของคุณ

PS

เป็นเพียงแนวทางปฏิบัติที่ดีขึ้น หวังว่าจะช่วยได้ คำตอบยืมมาจากที่นี่: เรียกใช้กรณีทดสอบเฉพาะใน Django เมื่อแอปของคุณมีไดเรกทอรีการทดสอบ


0

ฉันมีสองไฟล์ หนึ่งคือและอีกอย่างก็คือtests.py test_api.pyฉันสามารถเรียกใช้ทีละรายการดังต่อไปนี้

   manage.py test companies.tests
   manage.py test companies.test_api

อ้างอิงคำตอบของ @ osa เกี่ยวกับหลักการตั้งชื่อไฟล์

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