#pytest.fixture(scope="function",
params=load_json("path_to_json.json"))
def valid_data(self, request):
return request.param
So thats one fixture in one of my test class. They contain my expected test data. Before each test, i need to modify those json file.
#pytest.fixture(scope="session", autouse=True)
def prepare_file():
// Doing the change and writing it to the json file
But when i run the test, it seem the file are not getting update. But when the test finish. They are updated. What is happening ?
Some things you should understand:
Your fixture scopes definitely need to match if you want to use one inside of the other
Your individual fixtures can access other fixtures if you pass them along
I am not entirely sure if this solves your question, but:
import json
#pytest.fixture(scope="function"):
def output_json_filepath():
return 'path/to/file'
#pytest.fixture(scope="function"):
def json_data(request):
return request.param
#pytest.fixture(scope="function"):
def prepared_data(json_data):
# do something here?
return prepared_data
# Not sure why you need this...
#pytest.fixture(scope="function"):
def dump_data(prepared_data, output_json_filepath):
with io.BytesIO(output_json_filepath, 'wb') as stream:
stream.write(prepared_data)
...
#pytest.mark.unit_test
def some_test(prepared_data):
# use your prepared_data here in your test.
Related
I am working on some functional tests for my application. Depending on the logged user's permissions, the sidebar will have different links. I am parameterizing them (hard coded) and running a test that works well (app is a webtest app):
endpoints = [
'/',
'/endpoint1',
'endpoint2',
...
]
#pytest.mark.parametrize('endpoint', endpoints)
def test_endpoints(endpoint, app):
res = app.get(endpoint).maybe_follow()
assert res.status_code == 200
I would like to avoid having to hard code the list of links for each type of user. Inside a fixture I can actually get them programmatically, so ideally I would like to parametrize the return value of this fixture in order to run the test function above:
#pytest.fixture
def endpoints(app):
res = app.get('/login').follow()
sidebar_links = []
for link in res.html.ul.find_all('a'):
if link.has_attr('href') and not link['href'].startswith('#'):
sidebar_links.append(link['href'])
return sidebar_links
Is this possible?
I would suggest that you use the pytest_configure() hook instead as this method will run before all your test methods. in conftest.py file you can keep a global variable as pytest.endpoints= [] then later in the hook method keep on appending the value of endpoints to this variable
something like this
pytest.endpoints= []
def pytest_configure(config,app):
res = app.get('/login').follow()
for link in res.html.ul.find_all('a'):
if link.has_attr('href') and not link['href'].startswith('#'):
pytest.endpoints.append(link['href'])
within the test method use the same variable as a parameter like below
#pytest.mark.parametrize("endpoint",pytest.endpoints)
def test_endpoints(endpoint):
Well i am not completely aware of your design so i cannot suggest any further but you can give this a try.
I am having issues properly patching an imported function in pytest. The function I want to patch is a function designed to do a large SQL fetch, so for speed I would like to replace this with reading a CSV file. Here is the code I currently have:
from data import postgres_fetch
import pytest
#pytest.fixture
def data_patch_market(monkeypatch):
test_data_path = os.path.join(os.path.dirname(__file__), 'test_data')
if os.path.exists(test_data_path):
mock_data_path = os.path.join(test_data_path, 'test_data_market.csv')
mock_data = pd.read_csv(mock_data_path)
monkeypatch.setattr(postgres_fetch, 'get_data_for_market', mock_data)
def test_mase(data_patch_market):
data = postgres_fetch.get_data_for_market(market_name=market,
market_level=market_level,
backtest_log_ids=log_ids,
connection=conn)
test_result= build_features.MASE(data)
However when I run this test I am getting a type error about calling a DataFrame:
TypeError: 'DataFrame' object is not callable
I know the csv can be read properly as I've tested that separately, so I assume something is wrong with how I am implementing the patch fixture, but I can't seem to work it out
Here, your call to monkeypatch.setattr is replacing any call to postgres_fetch.get_data_for_market with a call to mock_data.
This can't work since mock_data is not a function - its a DataFrame object.
Instead, in your call to monkeypatch.setattr, you need to pass in a function that returns the mocked data (i.e. the DataFrame object).
Hence, something like this should work:
#pytest.fixture
def data_patch_market(monkeypatch):
test_data_path = os.path.join(os.path.dirname(__file__), 'test_data')
if os.path.exists(test_data_path):
mock_data_path = os.path.join(test_data_path, 'test_data_market.csv')
mock_data = pd.read_csv(mock_data_path)
# The lines below are new - here, we define a function that will return the data we have mocked
def return_mocked(*args, **kwargs):
return mock_data
monkeypatch.setattr(postgres_fetch, 'get_data_for_market', return_mocked)
I asked the same question in GitHub.
I learned about pytest-helpers-namespace from s0undt3ch in his very helpful answer. However I found a usecase I cant seem to find an obvious workaround. Here is the paste of my original question on GitHub.
How can I use the fixtures already declared in my conftest within my helper functions?
I am have a large, memory heavy configuration object (for simplicity, a dictionary) in all test, but I dont want to tear it down and rebuild this object, thus scoped as session and reused. Often times, I want to grab values from the configuration object within my test.
I know reusing fixtures within fixtures, you have to pass a reference
# fixtures
#pytest.fixture(scope="session")
def return_dictionary():
return {
"test_key": "test_value"
}
#pytest.fixture(scope="session")
def add_random(return_dictionary):
_temp = return_dictionary
_temp["test_key_random"] = "test_random_value"
return _temp
Is it because pytest collects similar decorators, and analyzes them together? I would like someone's input into this. Thanks!
Here is a few files I created to demonstrate what I was looking for, and what the error I am seeing.
# conftest.py
import pytest
from pprint import pprint
pytest_plugins = ["helpers_namespace"]
# fixtures
#pytest.fixture(scope="session")
def return_dictionary():
return {
"test_key": "test_value"
}
# helpers
#pytest.helpers.register
def super_print(_dict):
pprint(_dict)
#pytest.helpers.register
def super_print_always(key, _dict=return_dictionary):
pprint(_dict[key])
# test_check.py
import pytest
def test_option_1(return_dictionary):
print(return_dictionary)
def test_option_2(return_dictionary):
return_dictionary["test_key_2"] = "test_value_2"
pytest.helpers.super_print(return_dictionary)
def test_option_3():
pytest.helpers.super_print_always('test_key')
key = 'test_key', _dict = <function return_dictionary at 0x039B6C48>
#pytest.helpers.register
def super_print_always(key, _dict=return_dictionary):
> pprint(_dict[key])
E TypeError: 'function' object is not subscriptable
conftest.py:30: TypeError
I'm writing tests for a post api, which returns the resource that gets created. But how do I pass this data to a fixture in python so it can cleanup after the test is completed
Cleanup:
#pytest.fixture(scope='function')
def delete_after_post(request):
def cleanup():
// Get ID of resource to cleanup
// Call Delete api with ID to delete the resource
request.addfinalizer(cleanup)
Test:
def test_post(delete_after_post):
Id = post(api)
assert Id
What is the best way to pass the response(ID) back to to the fixture for the cleanup to kick in. Don't want to do the cleanup as part of the test.
You can access that ID using request instance and use anywhere in your code by request.instance.variableName. Like, Suppose your method for deleting id delete(resource_id), here
conftest.py
import pytest
#pytest.fixture(scope='function')
def delete_after_post(request):
def cleanup():
print request.node.resourceId
# Get ID of resource using request.instance.resourceId
# Call Delete api with ID to delete the resource
request.addfinalizer(cleanup)
test file xyz_test.py
def test_post(delete_after_post,request):
request.node.resourceId='3'
I created a fixture that collects cleanup functions for this purpose:
import pytest
#pytest.fixture
def cleaner():
funcs = []
def add_func(func):
funcs.append(func)
yield add_func
for func in funcs:
func()
def test_func(cleaner):
x = 5
cleaner(lambda: print('cleaning', x))
This way you don't need a separate fixture for each use case.
The way I did was create a class called TestRunContext and set static variables to pass around data.
File: test_run_context.py
class TestRunContext:
id_under_test = 0
File: conftest.py
#pytest.fixture(scope='function')
def delete_after_post():
print('hello')
yield
url = 'http://127.0.0.1:5000/api/centres/{0}'.format(TestRunContext.id_under_test)
resp = requests.delete(url)
File: test_post.py
def test_creates_post(delete_after_post):
post_data ={
'name' : 'test',
'address1': 'test',
'city': 'test',
'postcode': 'test',
}
url = 'http://127.0.0.1:5000/api/centres'
data = requests.post(url, post_data)
TestRunContext.id_under_test = data.id
assert data
This works for me for now. But hoping to find a better solution than using ContextManager file. Really dont like this solution.
I have bunch of test methods that i need to run and then after each test i want to update my results somewhere else.
This is what i have:
#pytest.mark.testcasename('1234')
#pytest.mark.parametrize('lang',
["EN", "FR"])
def test_text(self, request, base_url, lang):
testrail_conn = TestrailHelper()
test_case_id = request.node.get_marker("testcasename").args[0]
base_url = base_url.replace("testEN", "testFR") if lang == "FR" else base_url
self.navigate(base_url)
self.wait_for_page_loaded()
results = self.check_text(lang)
try:
assert results
testrail_conn.update_test_status(test_case_id, test_result=1)
except AssertionError:
testrail_conn.update_test_status(test_case_id, test_result=5)
My problem is that i want the update_test_status to be in a teardown fixture where i can pass my test_result to it. This way i dont need to write same code for each test method..
Any ideas?
Thanks
You could store something on the request object, e.g. on request.node - see the docs. Alternatively, you could use your fixture (as an argument) from the test, and store something there - or make the fixture return/yield some kind of data structure to store things in.