2016-05-18 15 views
10

Tôi có một thư viện python với cấu trúc kho sau:Sử dụng py.test với mã thư viện biên soạn

repobase 
|- mylibrary 
| |- __init__.py 
|- tests 
    |- test_mylibrary.py 

Đến nay, chạy các bài kiểm tra có thể chỉ đơn giản được thực hiện bằng cách gọi py.test trong thư mục repobase. import mylibrary trong test_mylibrary.py sau đó sử dụng mã cục bộ trong repobase/mylibrary.

Bây giờ, tôi đã mở rộng thư viện để sử dụng mã được biên dịch. Do đó mã nguồn tại repobase/mylibrary không hoạt động theo cách riêng của nó. Tôi phải làm setup.py build. Điều này tạo ra repobase/build/lib.linux-x86_64-2.7/mylibrary.

Có cách nào hợp lý để sử dụng py.test thư mục này để nhập thư viện của tôi không? Với những hạn chế:

  1. Tôi không muốn bao gồm bất kỳ ma thuật sys.path/nhập khẩu test_mylibrary.py vì điều này có thể phá vỡ các xét nghiệm trong envrionments khác.

  2. Tôi không muốn từ bỏ khả năng chạy py.test từ repobase. Do đó việc sửa đổi PYTHONPATH không giúp ích gì vì . vẫn sẽ là số đầu tiên trong số sys.path. Và do đó, repobase/mylibrary sẽ được ưu tiên hơn repobase/build/lib.linux-x86_64-2.7/mylibrary.

Nếu không, cách chuẩn để thử nghiệm thư viện python cần xây dựng là gì?

+0

Không rõ ý bạn là gì ... "Tôi đã mở rộng thư viện để sử dụng mã được biên dịch ...", tức là phiên bản được biên dịch cung cấp giao diện giống như phiên bản Python hoặc phiên bản Python 'nhập 'phiên bản đã biên dịch? Nếu trước đây, sau đó bạn đang thử nghiệm hiệu quả hai thứ khác nhau, vì vậy có lẽ họ nên có tên khác nhau, ngay cả khi bộ thử nghiệm là giống hệt nhau, q.v. 'Pickle' của Python so với' cPickle'. Nếu sau này, họ chắc chắn sẽ có tên khác nhau. Một thành ngữ Python phổ biến là cho phần được biên dịch được thêm vào trước với dấu gạch dưới, q.v. 'Socket' so với' _socket' của Python. – Aya

+0

(tiếp theo) Dù bằng cách nào, có hai cách triển khai mô-đun khác nhau chia sẻ cùng tên là yêu cầu sự cố. Ngay cả khi giải pháp của bạn hoạt động cho tất cả các trường hợp có thể chạy trong ngày hôm nay, bạn không thể dự đoán tất cả các trường hợp thời gian chạy trong tương lai và bạn có thể sẽ nhập hoặc kiểm tra phiên bản sai mà không nhận ra nó. – Aya

+0

@Aya Tôi vừa mới triển khai. Mã được biên dịch * thay thế * một số mã python trước đây. Như tôi đã viết "Vì vậy, mã nguồn tại repobase/mylibrary không phải là chức năng của riêng mình." Tôi cần mã được biên dịch để có thể chạy thử nghiệm. –

Trả lời

5

Tôi nghĩ vấn đề của bạn đơn giản là py.test không sao chép đối tượng được chia sẻ được xây dựng vào thư mục gốc của kho lưu trữ của bạn.

Tôi chỉ cố gắng chạy UT thẳng từ Python wiki trên phần mở rộng thử nghiệm C sử dụng py.test như sau:

python setup.py build 
py.test test/examp_unittest.py 

này thất bại với AssertionError: No module named examp.

Tuy nhiên, khi tôi làm theo wiki với chữ cái (và chạy python setup.py test thay), tôi lưu ý rằng nó sao chép .so vào thư mục gốc (lưu ý dòng cuối cùng trước khi nó bắt đầu chạy thử nghiệm):

running test 
running egg_info 
writing examp.egg-info/PKG-INFO 
writing top-level names to examp.egg-info/top_level.txt 
writing dependency_links to examp.egg-info/dependency_links.txt 
reading manifest file 'examp.egg-info/SOURCES.txt' 
writing manifest file 'examp.egg-info/SOURCES.txt' 
running build_ext 
copying build/lib.linux-x86_64-2.6/examp.so -> 
runTest (test.examp_unittest.DeviceTest) ... ok 

---------------------------------------------------------------------- 
Ran 1 test in 0.001s 

OK 

Sau khi chạy nó trên hệ thống của tôi, bây giờ tôi có thể chạy py.test khá vui vẻ trên cùng một cơ sở mã - như hình dưới đây.

============================= test session starts ============================== 
platform linux2 -- Python 2.7.3, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 
rootdir: /tmp/sotest, inifile: 
collected 1 items 

test/examp_unittest.py . 

=========================== 1 passed in 0.01 seconds =========================== 

Do đó, giải pháp sao chép đối tượng được chia sẻ của bạn vào thư mục gốc của kho lưu trữ của bạn.

Để đảm bảo tôi đã chạy toàn bộ điều từ đầu, chỉ cần xây dựng tiện ích mở rộng, sao chép đối tượng được chia sẻ và sau đó chạy py.test. Điều này tất cả các công trình như mong đợi.

+0

@TimHoffmann Có điều gì tôi đã bỏ lỡ trong câu trả lời này không? –

+0

Tôi thực sự không thể nói, bởi vì tôi chưa có thời gian để thử nó (chủ nhà vào ngày mai). Tôi kết luận từ ví dụ của bạn, rằng tôi phải chạy 'python setup.py test'. Nhưng điều đó sẽ chạy tiêu chuẩn unittests đầu tiên (mà là lỗi thời, bởi vì nó sẽ không phát hiện và/hoặc xử lý chính xác tất cả các bài kiểm tra của tôi). Nó chỉ tốn thêm thời gian. Có lẽ sau đó tôi nên tích hợp pytest với setuptools (https://pytest.org/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner). –

+0

Tuy nhiên, nó cảm thấy hơi sai khi phải chạy tất cả các bài kiểm tra mỗi lần. Tôi thường muốn thay đổi một số mã, sau đó xây dựng (bởi vì tôi phải cho các công cụ biên dịch) và sau đó chạy một thử nghiệm duy nhất, ví dụ: 'py.test tests/test_mylibrary.py :: test_do_something'. Có lẽ tôi phải điều chỉnh 'python setup.py build' hoặc tạo một' python setup.py build_test' tùy chỉnh mà chỉ cần sao chép các tệp '.so' bắt buộc. Tôi thực sự tự hỏi tại sao tôi không thể tìm thấy bất cứ điều gì theo hướng đó bởi vì tôi sẽ cho rằng đó là một vấn đề tiêu chuẩn bạn gặp phải ngay sau khi bạn sử dụng mã py.test và biên dịch. –

3

Từ cuộc thảo luận trong trò chuyện, có vẻ như việc triển khai C chỉ cung cấp một tập con của chức năng triển khai Python.

Một giải pháp phổ biến là chia mô-đun sao cho các phần yêu cầu triển khai tối ưu tồn tại trong một mô-đun riêng biệt.

Hãy xem xét ví dụ cụ thể hơn về thư viện cần chuyển đổi giữa các định dạng hình ảnh khác nhau.

Giả sử bố trí của bạn trông như thế này ...

repobase 
|- image 
| |- __init__.py 
| |- pyJPEG.py 
|- build 
| |- lib.linux-x86_64-2.7 
|  |- cJPEG.so 
|- tests 
    |- test_image.py 

... bạn PYTHONPATH bao gồm /path/to/repobase:/path/to/repobase/build/lib.linux-x86_64-2.7, bạn cJPEG.so xuất khẩu ký jpeg_decompressjpeg_compress, và các tập tin của bạn trông như thế này ...

image/__ init__.py

# Load the C implementation if we have it, otherwise fall back to 
# a pure Python implementation 
try: 
    from cJPEG import jpeg_decompress, jpeg_compress 
except ImportError: 
    from pyJPEG import jpeg_decompress, jpeg_compress 

def load_image(filename): 
    data = open(filename, 'rb').read() 
    if filename.endswidth('.jpg'): 
     return jpeg_decompress(data) 
    else: 
     raise NotImplementedError 

def save_image(data, filename, filetype='JPEG'): 
    if filetype == 'JPEG': 
     data = jpeg_compress(data) 
    else: 
     raise NotImplementedError 
    open(filename, 'wb').write(data) 

image/pyJPEG.py

def jpeg_decompress(data): 
    # A pure Python implementation of a JPEG decoder 

def jpeg_compress(data): 
    # A pure Python implementation of a JPEG encoder 

Với loại này bố trí, các bộ kiểm tra không cần quan tâm liệu thư viện được xây dựng hay không - bạn có thể sử dụng bộ ứng dụng như nhau trong cả hai trường hợp, và sự hiện diện (hoặc vắng mặt) của cJPEG.so sẽ xác định phiên bản nào được kiểm tra.

Các vấn đề liên quan