How to mock a method of a dependent class? - python

I'm trying to test a method (Creator.do_a_call) which calls another class's method (Requester.make_request) which makes external calls (all the code is below). So I want to mock Request.make_request to return a mocked out response object so it doesn't do any external calls, but when I do that it gives me an error (see below)
Here's the code:
Requester.py:
from requests import Response, Session, Request
class Requester:
url: str = ''
def __init__(self, url: str):
self.url = url
def make_request(self, path: str, method: str = 'get', body: dict = None, custom_headers: dict = None) -> Response:
print("make_request")
url = self.url + path
session = Session()
request = Request(method, url, json=body)
response = session.send(request.prepare())
return response
Creator.py
from Requester import Requester
from http import HTTPStatus as status
from requests import Response
class Creator:
def do_a_call(self, url) -> Response:
print("do_a_call")
requester = Requester(url)
response = requester.make_request(
"/",
"GET"
)
print(type(response))
if response.status_code != status.OK and response.status_code != status.ACCEPTED:
raise Exception(response.status_code, response.text)
return response
main.py
from Creator import Creator
print("main")
creator = Creator()
response = creator.do_a_call("https://google.com")
print(response)
test.py
import unittest
from unittest.mock import patch
from Creator import Creator
from requests import Session
import requests_mock
class CreatorTest(unittest.TestCase):
def mocked_make_request(self, two, three):
session = Session()
mocker = requests_mock.Mocker(session=session)
adapter = requests_mock.Adapter()
adapter.register_uri('POST', 'mock://test.com', text="Invalid policy number supplied", status_code=400)
return mocker.post("mock://test.com", status_code=400)
#patch('Requester.Requester.make_request')
def test_do_a_call_side_effect(self, make_request):
creator = Creator()
make_request.side_effect = self.mocked_make_request
response = creator.do_a_call("mock://test.com")
self.assertEqual(200, response.status_code)
#patch('Requester.Requester.make_request')
def test_do_a_call_return_value(self, make_request):
creator = Creator()
make_request.return_value = self.mocked_make_request("", "")
response = creator.do_a_call("mock://test.com")
self.assertEqual(200, response.status_code)
if __name__ == "__main__":
unittest.main()
In my research I've found two ways that should work - changing the return_value of the mock, or changing the side_effect of the mock, both of which give me the same error:
Traceback (most recent call last):
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1325, in patched
return func(*newargs, **newkeywargs)
File "test.py", line 19, in test_do_a_call_side_effect
response = creator.do_a_call("mock://test.com")
File "/Users/citrja/Documents/git/python_test/Creator.py", line 14, in do_a_call
if response.status_code != status.OK and response.status_code != status.ACCEPTED:
AttributeError: '_Matcher' object has no attribute 'status_code'

I've found out what was wrong! I Was not attaching the adapter to the session, I had thought this happened automagically but apparently not. Here's the updated mocked_make_request function:
def mocked_make_request(self, two, three):
session = Session()
mocker = requests_mock.Mocker(session=session)
adapter = requests_mock.Adapter()
adapter.register_uri('GET', 'mock://test.com', text="Invalid policy number supplied", status_code=400)
session.mount("mock://", adapter)
return session.get("mock://test.com")

Related

Python pytest mocking

Sorry I am new to pytest and I am trying to mock a class but I could not make it work as expected.
Can someone help me please?
File test.py:
from easysnmp import Session
class testSnmp: # pylint: disable=too-few-public-methods
"""
class test snmp conifg
"""
def __init__(self, hostname="", **params):
self.hostname = hostname
self.session = Session(
hostname=self.hostname,
version=3,
security_level="auth_with_privacy",
security_username=params["snmp_username"],
auth_protocol="SHA",
auth_password=params["snmp_auth_pwd"],
privacy_protocol="AES",
privacy_password=params["snmp_priv_pwd"],
)
def run_snmp_check(self):
"""
snmp run snmp method
"""
result = {}
result["hostname"] = self.hostname
try:
data = self.session.walk("1.6.6.1.2.1.1.6")
return self.process_data(data)
except Exception:
result["output"] = "Login Failed"
return result
def process_data(self, data=None):
result = {}
result["output"] = []
if data:
if data[0].value == "test":
res = "login succeeded"
else:
res = "Login Failed"
result["hostname"] = self.hostname
result["output"] = res
return result
test file
import pytest
from unittest.mock import Mock, patch, call
import testSnmp
from easysnmp import Session
#mock.patch('test.Session.walk')
#mock.patch('test.Session')
def test_run_snmp_check(mock_walk, mock_sess):
params = {"snmp_username":"b", "snmp_auth_pwd":"c", "snmp_priv_pwd":"d"}
snmp = testSnmp("id_device", **params)
val = [{"id_device":"test"}]
mock_sess_response = Mock()
mock_sess_response.return_value = val
#mock_sess.assert_called_with()
resp = snmp.run_snmp_check()
print(resp)
assert resp == [{"id_device":"test"}]
I am trying to mock session object and walk method but it fails so trying to figure it out.
I have tried similar approach for requests module for http get and post and it works fine.

How to mock url with Pytest parametrize and responses

I am new to unit testing in Python. I am trying to mock a response, but the url doesn't get mocked returning error that the mock is not registered and gives me a hint to use the real URL, with the real one it works, but it needs to be mocked somehow. Tried out pytest parametrize without success.
This is what I tried so far:
FAKE_HOST = "https://fake-host.com"
#pytest.mark.parametrize(
("fake_url"),
[(FAKE_HOST, "https://fake-host.com")],
)
#responses.activate
def test_item(fake_url):
responses.add(
responses.GET,
f"{fake_url}/rest/info?name=item",
status=200,
)
resp = requests.get(
"https://{fake_url}/rest/info?name=item"
)
assert resp.status_code == 200
import requests
def example2():
r = requests.get("http://httpbin.org/" + "get")
if r.status_code == 200:
response_data = r.json()
return r.status_code, response_data["url"]
else:
return r.status_code, ""
def test_get_response_success(monkeypatch):
class MockResponse(object):
def __init__(self):
self.status_code = 200
self.url = "http://httpbin.org/get"
self.headers = {"foobar": "foooooo"}
def json(self):
return {"fooaccount": "foo123", "url": "https://fake-host.com"}
def mock_get(url):
return MockResponse()
monkeypatch.setattr(requests, "get", mock_get)
assert example2() == (200, "https://fake-host.com")
Have you considered using monkepyatching?

Python: Calling a function from another function [duplicate]

This question already has answers here:
NameError: global name 'myExample2' is not defined # modules
(5 answers)
Closed 3 years ago.
I am writing my first unittest in python I have already wrote a passing test with class and function but now I want to write unittest and it is failing as follow
import unittest
import requests
import time
import json
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
class ChromeSearch(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
driver = self.driver
base_url = 'http://127.0.0.1:8080/'
driver.get(base_url)
def test_get_users(self):
print("Planning to get users")
token = test_generate_token()
headers = {'Content-Type': 'application/json',
'Token': token}
r = requests.get('http://127.0.0.1:8080/api/users', headers=headers)
print"Get request for api Users"
print(r.text)
def test_generate_token():
print("Generating Token")
usernameStr = 'user1a'
passwordStr = 'user'
response = requests.get('http://127.0.0.1:8080/api/auth/token', auth=(usernameStr, passwordStr))
print(response)
data = response.json()
print(data)
if response.status_code == 200 and data["status"] == "SUCCESS":
token = data["token"]
else:
token = None
print("The request was not successful.")
print(token)
return token
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
so what I am doing is calling test_generate_token() from test_get_users(self) to get token and then using token to get users but it fails with following error
Generating Token
<Response [200]>
{u'status': u'SUCCESS', u'token': u'OTEzMjQ0NjY1OTQ3NzM5OTYyMTczNTM0NzA4MjM0OTQ2NTQ5NDA='}
OTEzMjQ0NjY1OTQ3NzM5OTYyMTczNTM0NzA4MjM0OTQ2NTQ5NDA=
.
DevTools listening on ws://127.0.0.1:50610/devtools/browser/eb427541-898f-4c1d-938d-064462f50a68
Planning to get users
E
======================================================================
ERROR: test_get_users (__main__.ChromeSearch)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".\test_demo_app.py", line 18, in test_get_users
token = test_generate_token()
NameError: global name 'test_generate_token' is not defined
I have tried to follow this Python: Passing variables between functions
Token Part is moved to Setup
import unittest
import requests
import time
import json
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
class ChromeSearch(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
driver = self.driver
base_url = 'http://127.0.0.1:8080/'
driver.get(base_url)
print("Generating Token")
usernameStr = 'user1a'
passwordStr = 'user'
response = requests.get('http://127.0.0.1:8080/api/auth/token', auth=(usernameStr, passwordStr))
print(response)
data = response.json()
print(data)
if response.status_code == 200 and data["status"] == "SUCCESS":
token = data["token"]
else:
token = None
print("The request was not successful.")
print(token)
def test_get_users(self):
print("Planning to get users")
headers = {'Content-Type': 'application/json',
'Token': token}
r = requests.get('http://127.0.0.1:8080/api/users', headers=headers)
print"Get request for api Users"
print(r.text)
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
Since test_generate_token is a member function, you need to call it as a member function.
token = self.test_generate_token().

How to get content from tornado future object

I'm really confused by the tornado frame work and the 'future' object.
So I want to get a async response by making a http call
Code is:
class TestAsyncHttp(object):
def __init__(self):
self._http_client = httpclient.AsyncHTTPClient()
#gen.coroutine
def get_response(self, params)
response = yield self._request(
method='POST',
endpoint='test'
data=params
)
raise gen.Return(response)
#gen.coroutine
def _request(self, method, endpoint, data):
url = self._make_url(endpoint) #this includes the port..
headers = self._load_headers()
request = httpclient.HTTPRequest(
url,
method=method,
headers=header,
body=json.dump(data)
)
response = yield self._http_client.fetch(request)
raise gen.Return(response)
The thing is, after I finished this one, how can I test it?
I tried to write a scrip which contains...:
import json
with open('test/request.json') as json_file:
request_json = json.loads(json_file.read())
def get_response():
x = TestAsyncHttp()
ret = yield x.get_response(request_json)
body = ret.body
print body['value']
get_response
But then I 'python "path-to-the-script"'
There's nothing output.
If I just stepped into the "python" environment, I got "future" object doesn't have getitem
..How can I get the content from a future..?
Thanks!
Use run_sync to run an async coroutine in a synchronous fashion:
def get_response():
x = TestAsyncHttp()
ret = IOLoop.current().run_sync(lambda: x.get_response(request_json))
body = ret.body
print body['value']
The lambda is required here simply to pass the request_json parameter. If get_response took no arguments, you could instead do:
ret = IOLoop.current().run_sync(x.get_response)

Parse Header in Post Call Python

I am using rest client in my mozilla browser to call an auth service.
When i pass my credentials in Body, i get an "auth-token" . I then set this token in the header in the browser HEADERS tab.
I have to parse this header which i am setting in the browser in my python script as a variable. Further, after getting this value in my script i have to authenticate the token for its validity.
However i am unable to get the tokens value in my script. My auth function is ready. I just have to fetch the token
How should i fetch this token value from the header ??
Code:
def check_authentication(auth):
print "Auth" , auth
chek_auth_url = ("http://10.168.2.161/auth/v/%s" % (auth))
auth = requests.get(chek_auth_url)
if auth.status_code == 200:
return True
I have to pass the token as a paramter in this function and call in this function in main for authentication.
def crossdomain(origin=None, methods=None, headers=None, max_age=21600, attach_to_all=True, automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
#h['Access-Control-Allow-Headers'] = "Content-Type"
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
#app.route('/test', methods=['POST', 'OPTIONS'])
#crossdomain(origin='*', headers='Content-Type')
def get_storage():
*check_authentication is called here and token is passed as a parameter*
*if token is valid further task i hav to do*
if __name__ == '__main__':
app.run(host='192.168.56.1', port=8080, threaded=True)
Self-Help is the best help..
Finally i found a fix:
The token value is fetched in the variable tokenValue. I can now do my further coding.
tokenValue = request.headers.get("token")
if tokenValue == None:
return "x-auth-token not passed in header, please pass the token."
else:
print "Token passed is", tokenValue

Categories

Resources