Pytest Fixture Pass In Multiple Parameters - python

I am wanting to do something along the lines of:
I have a client fixture that takes in two parameters, url & is_caching
the url parameter is sent in using pytest.mark.parameterize
however the is_caching is determined by the function
How can I pass in the is_caching parameter in to the fixture, given that I also want the function name to have "names" as the suffix during collection
test_logging_disabled[n1]
test_logging_disabled[n2]
import pytest
#pytest.fixture(scope = "session", autouse = True)
def client(url, is_caching = True):
kwargs = {'is_caching': is_caching}
client = Client(url, **kwargs)
yield client
client.close()
#pytest.mark.parameterize("url", ["n1","n2"], ids = ["n1","n2"], indirect = True):
def test_caching_disabled(client):
#test

Related

Mock.patch returning MagicMock object causing AssertionError?

I have a function that I am trying to test in querySomething.py:
class QuerySomething:
def retrieveIssues(self,token):
responses = []
if "customFields" in self._event:
if not self.custom_fields:
fields = []
else:
fields = self.custom_fields
else:
fields = []
for issueTypeKey, issueTypeValue in self.issueTypes.items():
print(issueTypeKey, ":", issueTypeValue)
query = self.getQuery(issueTypeValue, self.status, fields)
respons = httpClient.get_request(query, token)
responses.append(respons)
return responses
And the test file:
def mock_getQuery():
return "QUERY"
def mock_response(state):
if state=="unauth":
with open("src/tests/mockdata/unauthorized_api_response.json","r") as response_file:
unauth_error = response_file.read()
return json.dumps(unauth_error)
elif state=="auth":
with open("src/tests/mockdata/success_api_response.json","r") as response_file:
success_message = response_file.read()
return json.dumps(success_message)
return "No message"
class test_query(unittest.TestCase):
#mock.patch("querySomething.QuerySomething.getQuery", side_effect=mock_getQuery)
#mock.patch("httpClient.get_request", side_effect=mock_response)
def test_retreiveIssues_unauth_response(self,mock_get,QuerySomething):
self.assertEqual(QuerySomething.retrieveIssues("token"),mock_response("unauth"))
if __name__ == "__main__":
unittest.main()
I am trying to mock the httpClient.get_request so that it gets the JSON file instead of reaching out to the API. We want to test an unauthorized response and a success response which explains the mock_response function. However, when I run the test, I get the following:
AssertionError: <MagicMock name='getQuery.retri[36 chars]712'> != '"{\\n \\"errorMessages\\": [\\n [131 chars]\n}"'
which is somewhat correct, but we need just the text, not the object. I read that I need to call the function, but when I try to call the function it throws a ModuleNotFound or NotAPackage error. What do I need to do to mock the httpClient.get_request and return the JSON string in the retrieveIssues function?
Updated, I was able to pull the JSON from the other file, and then was able to mock the return value as follows:
QuerySomething.retrieveIssues.return_value=load_json("unauth")
where load_json("unauth") pulls from the JSON response file.

How change one value to another in one place and use it in couple functions?

I'm writing test automation for API in BDD behave. I need a switcher between environments. Is any possible way to change one value in one place without adding this value to every functions? Example:
I've tried to do it by adding value to every function but its makes all project very complicated
headers = {
'Content-Type': 'application/json',
'country': 'fi'
}
what i what to switch only country value in headers e.g from 'fi' to 'es'
and then all function should switch themselves to es environment, e.g
def sending_post_request(endpoint, user):
url = fi_api_endpoints.api_endpoints_list.get(endpoint)
personalId = {'personalId': user}
json_post = requests.post(url,
headers=headers,
data=json.dumps(personalId)
)
endpoint_message = json_post.text
server_status = json_post.status_code
def phone_number(phone_number_status):
if phone_number_status == 'wrong':
cursor = functions_concerning_SQL_conection.choosen_db('fi_sql_identity')
cursor.execute("SELECT TOP 1 PersonalId from Registrations where PhoneNumber is NULL")
result = cursor.fetchone()
user_with_no_phone_number = result[0]
return user_with_no_phone_number
else:
cursor = functions_concerning_SQL_conection.choosen_db('fi_sql_identity')
cursor.execute("SELECT TOP 1 PersonalId from Registrations where PhoneNumber is not NULL")
result = cursor.fetchone()
user_with_phone_number = result[0]
return user_with_phone_number
and when i will change from 'fi' to 'es' in headers i want:
fi_sql_identity change to es_sql_identity
url = fi_api_endpoints.api_endpoints_list.get(endpoint) change to
url = es_api_endpoints.api_endpoints_list.get(endpoint)
thx and please help
With respect to your original question, a solution for this case is closure:
def f(x):
def long_calculation(y):
return x * y
return long_calculation
# create different functions without dispatching multiple times
g = f(val_1)
h = f(val_2)
g(val_3)
h(val_3)
Well, the problem is why do you hardcode everything? With the update you can simplify your function as:
def phone_number(phone_number_status, db_name='fi_sql_identity'):
cursor = functions_concerning_SQL_conection.choosen_db(db_name)
if phone_number_status == 'wrong':
sql = "SELECT TOP 1 PersonalId from Registrations where PhoneNumber is NULL"
else:
sql = "SELECT TOP 1 PersonalId from Registrations where PhoneNumber is not NULL"
cursor.execute(sql)
result = cursor.fetchone()
return result[0]
Also please don't write like:
# WRONG
fi_db_conn.send_data()
But use a parameter:
region = 'fi' # or "es"
db_conn = initialize_conn(region)
db_conn.send_data()
And use a config file to store your endpoints with respect to your region, e.g. consider YAML:
# config.yml
es:
db_name: es_sql_identity
fi:
db_name: fi_sql_identity
Then use them in Python:
import yaml
with open('config.yml') as f:
config = yaml.safe_load(f)
region = 'fi'
db_name = config[region]['db_name'] # "fi_sql_identity"
# status = ...
result = phone_number(status, db_name)
See additional useful link for using YAML.
First, provide an encapsulation how to access the resources of a region by providing this encapsulation with a region parameter. It may also be a good idea to provide this functionality as a behave fixture.
CASE 1: region parameter needs to vary between features / scenarios
For example, this means that SCENARIO_1 needs region="fi" and SCENARIO_2 needs region="es".
Use fixture and fixture-tag with region parameter.
In this case you need to write own scenarios for each region (BAD TEST REUSE)
or use a ScenarioOutline as template to let behave generate the tests for you (by using a fixture-tag with a region parameter value for example).
CASE 2: region parameter is constant for all features / scenarios (during test-run)
You can support multiple test-runs with different region parameters by using a userdata parameter.
Look at behave userdata concept.
This allows you to run behave -D region=fi ... and behave -D region=es ...
This case provides a better reuse of testsuite, meaning a large part of the testsuite is the common testsuite that is applied to all regions.
HINT: Your code examples are too specific ("fi" based) which is a BAD-SMELL.

Python Mock: Result of method call is __getitem__ instead of actual value

I have a unit test and mocking an external call. Here's the abbreviated code for the function I'm trying to test in service.py file
def post_data()
req = request.Request()
response = req.post(payload, url, json.dumps({"data": kwargs['data']}))
if response['request']['status'] == 'SUCCESS' and response['data']:
run_id = response.json()['data']['run_id']
response = track_run_to_completion(run_id, **kwargs)
return response
Here's my unit test method
#patch('service.request.Request.post')
def test_post_data(self, mock_post):
kwargs = {'a':'abc'}
expected = json.dumps({'request':{'status':'ERROR'},'data':{}})
mock_post.return_value = MagicMock(status_code=200, response=expected)
mock_post.assert_called_once_with({'action': 'trigger'}, 'a/abc', '{"data": {}}') # SUCCESS!
result = service.post_data(**kwargs)
print result
When I print the result, I was expecting to see the json, but get <MagicMock name='post()' id='4488707600'>. What am I missing here? I'm new to Python and started writing unit tests for an existing application.

defined function got an unexpected keyword argument

I've got a problem with this line:
processed = process(cleaned, lemmatizer=nltk.stem.wordnet.WordNetLemmatizer());
Why is the unexpected keyword argument popping up?
Error: TypeError: process() got an unexpected keyword argument 'lemmatizer'
Here is my code:
def process(text, filters=nltk.corpus.stopwords.words('english')):
""" Normalizes case and handles punctuation
Inputs:
text: str: raw text
lemmatizer: an instance of a class implementing the lemmatize() method
(the default argument is of type nltk.stem.wordnet.WordNetLemmatizer)
Outputs:
list(str): tokenized text
"""
lemmatizer=nltk.stem.wordnet.WordNetLemmatizer()
word_list = nltk.word_tokenize(text);
lemma_list = [];
for i in word_list:
if i not in filters:
try:
lemma = lemmatizer.lemmatize(i);
lemma_list.append(str(lemma));
except:
pass
return " ".join(lemma_list)
if __name__ == '__main__':
#construct filter for processor
file = open("accountant.txt").read().lower()
filters = set(nltk.word_tokenize(file))
filters.update(nltk.corpus.stopwords.words('english'))
filters = list(filters)
#webcrawling
webContent = []
dataJobs = pd.read_csv("test.csv");
webContent = []
for i in dataJobs["url"]:
content = webCrawl(i);
webContent.append(content);
#clean the crawled text
cleaned_list = []
for j in webContent:
cleaned = extractUseful(j);
processed = process(cleaned, lemmatizer=nltk.stem.wordnet.WordNetLemmatizer());
cleaned_list.append(processed)
#save to csv
contents = pd.DataFrame({ "Content":webContent, "Cleaned": cleaned_list})
contents.to_csv("testwebcrawled.csv")
dataJobs[['jd']]= cleaned_list
dataJobs.to_csv("test_v2_crawled.csv")
You only define one keyword argument filters in the function signature for process (the def process(...) line). If the lemmatizer is what you intend to pass as the filter try:
processed = process(cleaned, filter=nltk.stem.wordnet.WordNetLemmatizer())
If you want to be able to pass a lemmatizer as well, you should change your function signature to something like this:
def process(text,
filters=nltk.corpus.stopwords.words('english'),
lemmatizer=nltk.stem.wordnet.WordNetLemmatizer()):
But note that you only need the = and the content that follows it in your function signature if you want the values after the = to be passed as the default arguments for these parameters. Otherwise, you can just do:
def process(text, filter, lemmatizer):
...
And call it like:
processed = process(cleaned,
filter=nltk.corpus.stopwords.words('english'),
lemmatizer=nltk.stem.wordnet.WordNetLemmatizer())

How does iPython Parallel's map function execute a function?

I'm using ipython's parallel features:
client = Client()
client.direct_view().use_dill()
def hostname():
import socket
return socket.gethostname()
# For joblib, we only want one client per host.
joblib_clients = dict(zip(map(lambda x: client[x].apply_sync(hostname), client.ids), client.ids))
lview_joblib = client.load_balanced_view(targets=joblib_clients.values())
dview = client.direct_view()
I also read in data from disk locally and on each engine
%%px --local
store = pd.HDFStore(data_file, 'r')
rows = store.select('results', ['cv_score_mean > 0'])
rows = rows.sort('cv_score_mean', ascending=False)
rows['results_index'] = rows.index
data_model = store.select('data_model')
p = re.compile('_coef$')
feature_set = rows.filter(regex='_coef$').dropna(axis=1, how='all').rename(columns=lambda x: p.sub('',x)).columns
Then I define and instantiate a class on all the engines (but not locally).
%%px
class DataRegression(object):
def __init__(self, **kwargs): ...
...
def regress_z_scores(self, **kwargs):
...
regressions = DataRegression(data_model, feature_set)
There are a few ways I can think to call this function through my load-balanced view:
1) Through a lambda function:
# What exactly is this lambda function passing to the lview?
ar = lview_joblib.map(lambda run: regressions.regress_z_scores(run), runs)
2) By trying to invoke the function directly:
# This fails with a NameError, because regressions.regress_z_scores is not defined
ar = lview_joblib.map(regressions.regress_z_scores, runs)
3) By creating the regressions object locally as well:
%%px --local
class DataRegression(object):
def __init__(self, **kwargs): ...
...
def regress_z_scores(self, **kwargs):
...
regressions = DataRegression(data_model, feature_set)
# And invoking it through the name.
ar = lview_joblib.map(regressions.regress_z_scores, runs)
# Does this mean that the local object gets pickled and passed to the client each time?
In each of these cases, how is the load-balanced view's map function actually executing this function call? Is there a best practice?

Categories

Resources