Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to skip a pytest using an external fixture?

Background

I am running a py.test with a fixture in a conftest file. You can see the code below(this all works fine):

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" 

Problem

Now I want to be able to skip some tests that do not apply to my current test-run. In my example I am running tests either for iOS or Android (This is just for demonstration purposes only and could be any other expression).

Unfortunately I cannot get ahold of (my externally defined fixture) platform_external in the skipif statement. When I run the code below I receive the following exception: NameError: name 'platform_external' is not defined. I don't know if this is a py.test bug as locally defined fixtures are working.

add-on for 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') 

So I thought I will just create my own decorator, just to see that it won't receive the fixtures as parameters:

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

Question

How can I define a fixture in a conftest file and use it to (conditionally) skip a test?

like image 609
Marco Pashkov Avatar asked Jan 27 '15 20:01

Marco Pashkov


People also ask

How do I skip a pytest test?

The simplest way to skip a test function is to mark it with the skip decorator which may be passed an optional reason : @pytest. mark.

Can a pytest fixture use another fixture?

One of the things that makes pytest's fixture system so powerful, is that it gives us the abilty to define a generic setup step that can reused over and over, just like a normal function would be used. Two different tests can request the same fixture and have pytest give each test their own result from that fixture.

Which of the following decorator is used to skip test unconditionally with pytest?

You can mark a test with the skip and skipif decorators when you want to skip a test in pytest .

Can I use fixture in Parametrize pytest?

Being able to reuse fixtures in parametrized tests is a must when we want to avoid repetition. Unfortunately, pytest doesn't support that yet. On the other hand, we can make it happen either by using getfixturevalue in pytest or through a third-party library.


2 Answers

It seems py.test doesn't use the test fixtures when evaluating the expression for skipif. By your example, test_ios is actually successful because it is comparing the function platform found in the module's namespace to the "ios" string, which evaluates to False hence the test is executed and succeeds. If pytest was inserting the fixture for evaluation as you expect, that test should have been skipped.

A solution to your problem (not to your question though) would be to implement a fixture that inspects marks into the tests, and skips them accordingly:

# conftest.py import pytest  @pytest.fixture def platform():     return "ios"  @pytest.fixture(autouse=True) def skip_by_platform(request, platform):     if request.node.get_closest_marker('skip_platform'):         if request.node.get_closest_marker('skip_platform').args[0] == platform:             pytest.skip('skipped on this platform: {}'.format(platform))    

A key point is the autouse parameter, which would make that fixture to be automatically included by all tests. Then your tests can mark which platforms to skip like this:

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

Hope that helps!

like image 185
Bruno Oliveira Avatar answered Oct 12 '22 03:10

Bruno Oliveira


The Solution from Bruno Oliveira is working, but for new pytest (>= 3.5.0) you need to add the pytest_configure:

 # conftest.py import pytest  @pytest.fixture def platform():     return "ios"  @pytest.fixture(autouse=True) def skip_by_platform(request, platform):     if request.node.get_closest_marker('skip_platform'):         if request.node.get_closest_marker('skip_platform').args[0] == platform:             pytest.skip('skipped on this platform: {}'.format(platform))     def pytest_configure(config):   config.addinivalue_line(         "markers", "skip_by_platform(platform): skip test for the given search engine",   ) 

Use:

@pytest.mark.skip_platform('ios') def test_ios(platform, request):     assert 0, 'should be skipped'  
like image 25
user1921483 Avatar answered Oct 12 '22 02:10

user1921483