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?
Related
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.
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")
I'm currently using Flask-HTTPAuth for basic authentication within my project. I've tested it by hand using curl and base64 tokens and it does indeed work. However I'm running into problems creating tests proving it works. This is my current test and it always turns back 401:
class TestLoginApi
def setup(self):
myapp.app.config.from_object("config.TestingConfig")
self.app = myapp.app.test_client()
client = MongoClient(myapp.app.config['DATABASE_URL'])
db = client.get_default_database()
assert list(db.collection_names()) == []
db.users.insert_one({'name': 'testuser', 'password': 'testpassword'})
def teardown(self):
client = MongoClient(myapp.app.config['DATABASE_URL'])
client.drop_database(client.get_default_database())
def test_not_logged_in(self):
rv = self.app.get('/api/v1/login/')
assert rv.status_code == 401
def test_correct_password(self):
d = Headers()
d.add('Authorization', 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk')
rv = self.app.get('/api/v1/login/', headers=d,
content_type='application/json')
assert rv.status_code == 200
I've also tried changing the following:
def test_correct_password(self):
d = Headers()
d.add('Authorization', 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk')
rv = self.app.get('/api/v1/login/', headers=d,
content_type='application/json')
assert rv.status_code == 200
to this with no success:
def test_correct_password(self):
rv = self.app.get('/api/v1/login/', headers={"Authorization": "Basic {user}".format(user=base64.b64encode(b"testuser:testpassword"))})
assert rv.status_code == 200
You can test authorization like this:
import base64
valid_credentials = base64.b64encode(b"testuser:testpassword").decode("utf-8")
response = self.app.get(
"/api/v1/login/",
headers={"Authorization": "Basic " + valid_credentials}
)
assert response.status_code == 200
I think you would enjoy my github project, which has a tons of different tests for Flask-HTTPAuth.
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
So I have 3 def tests (test1,test2 and test3) below, I am focusing on def test3, where, I want to be able to check if a user IS NOT in the database, does anybody know how I would go about doing that?
Code:
'''
security service tests
'''
import sys
sys.path.append('..')
import requests
import json
from common.constants import *
from config.settings import environment
security_environment = environment()['security_service']
service_url = "%s://%s:%d" % (
security_environment['protocol'],
security_environment['host'],
security_environment['port'])
def test1():
print "TEST 1 - get user details"
headers = {'Accept': 'application/json'}
url = "%s/user/1" % service_url
response = requests.get(url, headers=headers)
status_code = response.status_code
print "STATUS: %s" % status_code
print "DATA: %s" % response.json()
return status_code == HTTP_OK
def test2():
print "TEST 2 - Count amount of Users"
headers = {'Accept': 'application/json'}
url = "%s/users" % service_url
response = requests.get(url, headers=headers)
status_code = response.status_code
data = response.json()
print (len(data))
if status_code != HTTP_OK:
return False
return len(data) == 5
def test3():
print "TEST 3 - Check if user is NOT in the Database"
headers = {'Accept': 'application/json'}
url = "%s/users" % service_url
response = requests.get(url, headers=headers)
status_code = response.status_code
users = response.json()
if users is None:
status_code != HTTP_OK
else:
status_code == HTTP_OK
if __name__ == "__main__":
num_pass = 0
num_fail = 0
for test in [test1, test2, test3]:
print "-----------------------------------------------------------"
if test():
num_pass += 1
print "PASS"
else:
num_fail += 1
print "FAIL"
print "==========================================================="
print "%d passed, %d failed" % (num_pass, num_fail)
Give us sample of your response.
If your json data looks like this one:
objUser = [{'login': 'monkey1', 'name': 'Adam'},
{'login': 'monkey2', 'name': 'Maria'}]
Try somting like this:
def test3(user):
print "TEST 3 - Check if user is NOT in the Database"
headers = {'Accept': 'application/json'}
url = "%s/users" % service_url
response = requests.get(url, headers=headers)
status_code = response.status_code
if status_code == 200:
print 'Status: OK'
# json() method return False if no json in respone
users = response.json()
if users:
# covert json string to python object
objUsers = json.loads(users)
# iter users collection
result = [u['login'] for u in objUsers if u['login'] == user]
# if result empty return true, user IS NOT in reponse
if result:
return False
else:
return True
else:
print 'No json data in response.'
else:
print 'Couldn\'t get response from server.'