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?
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
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()
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
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
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()}
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.