web-dev-qa-db-fra.com

Comment partager des variables globales entre tests?

J'ai une variable globale dans conftest.py et je l'utilise dans les tests. Par exemple:

conftest.py

api_version = 'v25'
api_url = 'http://www.foobar.com/' + api_version

test_foo.py

from conftest import api_url
import requests

@pytest.fixture
def data():
    return requests.request("GET", api_url)

test_bar(data):
    assert data is not None

Maintenant, je veux pouvoir changer api_version de cmd pour tester une autre version d'api. J'ai donc modifié conftest.py de la manière suivante:

conftest.py

api_url = None

def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")

@pytest.fixture(autouse=True, scope='session')
def cmd_param(pytestconfig):
    api_version = pytestconfig.getoption("--mobile_api_ver").lower()
    global api_url
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        api_url = 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)

Mais cela ne fonctionne pas comme prévu car toutes les importations s'exécutent avant les fixtures et l'importation test_foo api_url = None before cmd_param fixture redefines ce. Ensuite, j'écris la méthode get_api_url et l'appelle à partir du module de test:

conftest.py

api_url = None

def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")

@pytest.fixture(autouse=True, scope='session')
def cmd_param(pytestconfig):
    api_version = pytestconfig.getoption("--mobile_api_ver").lower()
    global api_url
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        api_url = 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)

def get_api_url():
    return api_url

Mais maintenant, j'ai aussi été obligé de changer test_foo.py:

test_foo.py

from conftest import get_api_url
import requests

@pytest.fixture
def data():

    return requests.request("GET", get_api_url())

test_bar(data):
    assert data is not None

Cela fonctionne, mais la solution semble maladroite. Existe-t-il un moyen plus élégant d'utiliser des options cmd personnalisées sans modifier les fichiers de test?

7
Andrey Glazkov

Remarque: pytest_namespace est désormais obsolète

pytest fournit un moyen d'utiliser certaines variables globales dans la session. Ces variables peuvent également être utilisées par les appareils.

Ces variables sont contrôlées via des hameçons pytest.

import pytest

def pytest_namespace():
    return {'my_global_variable': 0}

@pytest.fixture
def data():
    pytest.my_global_variable = 100

def test(data):
    print pytest.my_global_variable
7
supamaze

Selon docs , pytest_namespace a été supprimé dans la version 4.0:

On peut utiliser pytest_configure pour partager des variables globales.

Exemple:

import pytest

def pytest_configure():
    pytest.my_symbol = MySymbol()
5
yashtodi94

Je ne jouerais pas avec les variables globales. Définissez simplement votre appareil pour renvoyer une valeur et utilisez cet appareil dans vos tests: Similaire à ce que @milo a publié mais beaucoup plus simple.

Vous aviez également défini --api_version Option CLI mais accès à --mobile_api_ver option dans votre appareil. De plus, votre test vérifie simplement qu'un objet de réponse n'est pas None, ce qui ne sera jamais None, donc l'instruction assert passera toujours même si la réponse a le statut 404, voir les commentaires en ligne.

Voici du code qui fonctionnera:

contenu de conftest.py

import pytest


def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")


@pytest.fixture(scope='session')
def api_url(pytestconfig):
    api_version = pytestconfig.getoption("--api_version").lower()
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        return 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)

contenu de test_foo.py

import pytest
import requests


@pytest.fixture
def data(api_url):  # probably a good idea to rename your fixture to a api_response or change what fixture returns.
    return requests.get(api_url)


def test_bar(data):
    print(data.text)
    # below you are not testing data, but merely checking that response object is not None
    assert data is not None  # this will always pass

    # you probably want to test status code and response content
    assert data.status_code == 200
    assert data.json()

Exécutez les tests: pytest -vvv --api_version v24 test_foo.py

3
Dmitry Tokarev

J'essaie juste de le faire fonctionner sans changer complètement votre code. J'espère que cela pourrait vous donner une idée.

dans conftest.py

api_url_by_option = None

def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")

@pytest.fixture(autouse=True, scope='session')
def cmd_param(pytestconfig):
    api_version = pytestconfig.getoption("--mobile_api_ver").lower()
    global api_url_by_option
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        api_url_by_option = 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)

@pytest.fixture:
def api_url():
    return api_url_by_option

dans test_foo.py, vous n'avez pas besoin d'importer api_url. Veuillez noter que le luminaire api_url de conftest.py est utilisé dans les données du luminaire

import requests

@pytest.fixture
def data(api_url):
    return requests.request("GET", api_url)

test_bar(data):
    assert data is not None
2
milo

Vous pouvez actuellement utiliser l'objet pytest directement comme indiqué dans la documentation, mais uniquement As a stopgap measure:

https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace

import pytest


def pytest_configure():
    pytest.my_symbol = MySymbol()

Mais attention si vous utilisez la version namespace car elle est obsolète: https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace

ancienne version utilisant l'espace de noms:

class MySymbol:
    ...


def pytest_namespace():
    return {"my_symbol": MySymbol()}
0
Joe Gasewicz

Ce que je fais dans conftest.py:


class StoreStuffHere:
    something_to_start_with = "value"
    somethingnew = None

#if you want an empty class:

class StoreStuffHere:
   pass

Ce que je fais dans test_sample.py:

from conftest import StoreStuffHere

store_stuff_here = StoreStuffHere

#this will pass
def test_assert_value_stored():
    store_stuff_here.somethingnew = 45
    assert store_stuff_here.something_to_start_with == "value"

#this will pass
def test_assert_fresh_stored_value():
    assert store_stuff_here.somethingnew == 45

Cela fonctionnera pour tous les tests du même module. Si vous souhaitez utiliser le même "stockage" entre les modules de test, utilisez plutôt un dictionnaire ou un tupple nommé au lieu de la classe que j'ai utilisée. Afin de vous assurer que vous n'obtenez pas d'erreurs de valeurs manquantes lorsque certains tests échouent, veuillez initialiser toutes les valeurs connues avec Aucun.

0
Alexandru Berbier