2015-01-27 14 views
7

nền

Tôi đang chạy một py.test với một fixture trong một conftest file. Bạn có thể xem mã dưới đây (điều này tất cả hoạt động tốt):Làm thế nào để bỏ qua một pytest bằng cách sử dụng một vật cố định bên ngoài?

example_test.py

import pytest 

@pytest.fixture 
def platform(): 
    return "ios" 

@pytest.mark.skipif("platform == 'ios'") 
def test_ios(platform): 
    if platform != 'ios': 
     raise Exception('not ios') 

def test_android_external(platform_external): 
    if platform_external != 'android': 
     raise Exception('not android') 

conftest.py

import pytest 

@pytest.fixture 
def platform_external(): 
    return "android" 

Vấn đề

Bây giờ tôi muốn có thể bỏ qua một số bài kiểm tra không áp dụng cho bài kiểm tra hiện tại của tôi. Trong ví dụ của tôi, tôi đang chạy thử nghiệm cho iOS hoặc Android (Đây chỉ là mục đích minh họa và có thể là bất kỳ biểu thức nào khác).

Đáng tiếc là tôi không thể có được Ahold của (tôi bên ngoài định nghĩa cố) platform_external trong báo cáo skipif. Khi tôi chạy mã bên dưới, tôi nhận được ngoại lệ sau: NameError: name 'platform_external' is not defined. Tôi không biết đây có phải là lỗi py.test như cục bộ các đồ đạc đã xác định đang hoạt động hay không.

add-on cho example_test.py

@pytest.mark.skipif("platform_external == 'android'") 
def test_android(platform_external): 
    """This test will fail as 'platform_external' is not available in the decorator. 
    It is only available for the function parameter.""" 
    if platform_external != 'android': 
     raise Exception('not android') 

Vì vậy, tôi nghĩ rằng tôi sẽ chỉ cần tạo riêng trang trí của tôi, chỉ để thấy rằng nó sẽ không nhận được đồ đạc như các thông số:

from functools import wraps 

def platform_custom_decorator(func): 
    @wraps(func) 
    def func_wrapper(*args, **kwargs): 
     return func(*args, **kwargs) 
    return func_wrapper 

@platform_custom_decorator 
def test_android_2(platform_external): 
    """This test will also fail as 'platform_external' will not be given to the 
    decorator.""" 
    if platform_external != 'android': 
     raise Exception('not android') 

Câu hỏi

Làm cách nào tôi có thể xác định sửa đổi ture trong một tệp conftest và sử dụng nó để (có điều kiện) bỏ qua thử nghiệm?

Trả lời

15

Có vẻ như py.test không sử dụng đồ đạc thử nghiệm khi đánh giá biểu thức cho skipif. Theo ví dụ của bạn, test_ios thực sự thành công vì nó so sánh hàm platform được tìm thấy trong không gian tên của mô-đun với chuỗi "ios", được đánh giá là False do đó thử nghiệm được thực hiện và thành công. Nếu pytest đã được chèn các vật cố định để đánh giá như bạn mong đợi, kiểm tra đó nên đã được bỏ qua.

Một giải pháp cho vấn đề của bạn (không cho câu hỏi của bạn mặc dù) sẽ được thực hiện một vật cố mà kiểm tra dấu vào các bài kiểm tra, và bỏ qua chúng cho phù hợp:

# conftest.py 
import pytest 

@pytest.fixture 
def platform(): 
    return "ios" 

@pytest.fixture(autouse=True) 
def skip_by_platform(request, platform): 
    if request.node.get_marker('skip_platform'): 
     if request.node.get_marker('skip_platform').args[0] == platform: 
      pytest.skip('skipped on this platform: {}'.format(platform)) 

Một điểm quan trọng là autouse tham số, trong đó sẽ làm cho trận đấu đó được tự động bao gồm bởi tất cả các bài kiểm tra.Sau đó, các thử nghiệm của bạn có thể đánh dấu nền tảng nào sẽ bỏ qua như sau:

@pytest.mark.skip_platform('ios') 
def test_ios(platform, request): 
    assert 0, 'should be skipped' 

Hy vọng điều đó sẽ hữu ích!

+0

Cảm ơn bạn - Tôi cũng đã chọn điểm đánh dấu ngày hôm qua như một công trình xung quanh, nhưng không thích nó vì nó đã không thanh lịch như bạn. (Tôi đã sử dụng 'pytest_runtest_setup' để kiểm tra điểm đánh dấu). Nhưng cho ràng buộc py.tests này có vẻ như giải pháp gần nhất cho câu hỏi của tôi và tôi sẽ cập nhật câu hỏi của tôi để sắp xếp nó. –

0

Tôi gặp vấn đề tương tự và tôi không biết điều này có còn phù hợp với bạn hay không, nhưng tôi có thể đã tìm thấy cách giải quyết có thể làm những gì bạn muốn.

Ý tưởng là để mở rộng các lớp MarkEvaluator và ghi đè lên các phương pháp _getglobals để buộc để thêm giá trị cố định trong các thiết lập toàn cầu được sử dụng bởi đánh giá:

conftest.py

from _pytest.skipping import MarkEvaluator 

class ExtendedMarkEvaluator(MarkEvaluator): 
    def _getglobals(self): 
     d = super()._getglobals() 
     d.update(self.item._request._fixture_values) 
     return d 

thêm một cái móc để kiểm tra cuộc gọi:

def pytest_runtest_call(item): 
    evalskipif = ExtendedMarkEvaluator(item, "skipif_call") 
    if evalskipif.istrue(): 
     pytest.skip('[CANNOT RUN]' + evalskipif.getexplanation()) 

thì bạn có thể sử dụng điểm đánh dấu skipif_call trong trường hợp thử nghiệm của bạn:

test_example.py

class Machine(): 
    def __init__(self, state): 
     self.state = state 

@pytest.fixture 
def myfixture(request): 
    return Machine("running") 

@pytest.mark.skipif_call('myfixture.state != "running"') 
def test_my_fixture_running_success(myfixture): 
    print(myfixture.state) 
    myfixture.state = "stopped" 
    assert True 

@pytest.mark.skipif_call('myfixture.state != "running"') 
def test_my_fixture_running_fail(myfixture): 
    print(myfixture.state) 
    assert False 

@pytest.mark.skipif_call('myfixture.state != "stopped"') 
def test_my_fixture_stopped_success(myfixture): 
    print(myfixture.state) 
    myfixture.state = "running" 

@pytest.mark.skipif_call('myfixture.state != "stopped"') 
def test_my_fixture_stopped_fail(myfixture): 
    print(myfixture.state) 
    assert False 

Run

pytest -v --tb=line 
============================= test session starts ============================= 
[...] 
collected 4 items 

test_example.py::test_my_fixture_running_success PASSED 
test_example.py::test_my_fixture_running_fail FAILED 
test_example.py::test_my_fixture_stopped_success PASSED 
test_example.py::test_my_fixture_stopped_fail FAILED 

================================== FAILURES =================================== 
C:\test_example.py:21: assert False 
C:\test_example.py:31: assert False 
===================== 2 failed, 2 passed in 0.16 seconds ====================== 

Vấn đề

Thật không may, điều này chỉ hoạt động một lần cho mỗi biểu thức đánh giá từ MarkEvaluator sử dụng ca ched eval dựa trên biểu thức là khóa, do đó, lần sau cùng một biểu thức sẽ được kiểm tra, kết quả sẽ là giá trị được lưu trong bộ nhớ cache.

Giải pháp

Khái niệm được đánh giá trong phương pháp _istrue. Thật không may là không có cách nào để cấu hình bộ đánh giá để tránh kết quả bộ nhớ đệm. Cách duy nhất để tránh bộ nhớ đệm là để ghi đè lên _istrue phương pháp để không sử dụng chức năng cached_eval:

class ExtendedMarkEvaluator(MarkEvaluator): 
    def _getglobals(self): 
     d = super()._getglobals() 
     d.update(self.item._request._fixture_values) 
     return d 

    def _istrue(self): 
     if self.holder: 
      self.result = False 
      args = self.holder.args 
      kwargs = self.holder.kwargs 
      for expr in args: 
       import _pytest._code 
       self.expr = expr 
       d = self._getglobals() 
       # Non cached eval to reload fixture values 
       exprcode = _pytest._code.compile(expr, mode="eval") 
       result = eval(exprcode, d) 

       if result: 
        self.result = True 
        self.reason = expr 
        self.expr = expr 
        break 
      return self.result 
     return False 

Run

pytest -v --tb=line 
============================= test session starts ============================= 
[...] 
collected 4 items 

test_example.py::test_my_fixture_running_success PASSED 
test_example.py::test_my_fixture_running_fail SKIPPED 
test_example.py::test_my_fixture_stopped_success PASSED 
test_example.py::test_my_fixture_stopped_fail SKIPPED 

===================== 2 passed, 2 skipped in 0.10 seconds ===================== 

Bây giờ các cuộc thử nghiệm được bỏ qua vì giá trị 'myfixture' đã được cập nhật .

Hy vọng điều đó sẽ hữu ích.

Cheers

Alex

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