Python pytest mocking - python

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.

Related

How to: Spyne authentication?

I don't understand how to setup the user and password for authentication of my soap server.
I found this example:
import logging
import random
import sys
# bcrypt seems to be among the latest consensus around cryptograpic circles on
# storing passwords.
# You need the package from http://code.google.com/p/py-bcrypt/
# You can install it by running easy_install py-bcrypt.
try:
import bcrypt
except ImportError:
print('easy_install --user py-bcrypt to get it.')
raise
from spyne.application import Application
from spyne.decorator import rpc
from spyne.error import ArgumentError
from spyne.model.complex import ComplexModel
from spyne.model.fault import Fault
from spyne.model.primitive import Mandatory
from spyne.model.primitive import String
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from spyne.service import Service
class PublicKeyError(Fault):
__namespace__ = 'spyne.examples.authentication'
def __init__(self, value):
super(PublicKeyError, self).__init__(
faultstring='Value %r not found' % value)
class AuthenticationError(Fault):
__namespace__ = 'spyne.examples.authentication'
def __init__(self, user_name):
# TODO: self.transport.http.resp_code = HTTP_401
super(AuthenticationError, self).__init__(
faultcode='Client.AuthenticationError',
faultstring='Invalid authentication request for %r' % user_name)
class AuthorizationError(Fault):
__namespace__ = 'spyne.examples.authentication'
def __init__(self):
# TODO: self.transport.http.resp_code = HTTP_401
super(AuthorizationError, self).__init__(
faultcode='Client.AuthorizationError',
faultstring='You are not authozied to access this resource.')
class SpyneDict(dict):
def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
raise PublicKeyError(key)
class RequestHeader(ComplexModel):
__namespace__ = 'spyne.examples.authentication'
session_id = Mandatory.String
user_name = Mandatory.String
class Preferences(ComplexModel):
__namespace__ = 'spyne.examples.authentication'
language = String(max_len=2)
time_zone = String
user_db = {
'neo': bcrypt.hashpw('Wh1teR#bbit', bcrypt.gensalt()),
}
session_db = set()
preferences_db = SpyneDict({
'neo': Preferences(language='en', time_zone='Underground/Zion'),
'smith': Preferences(language='xx', time_zone='Matrix/Core'),
})
class AuthenticationService(Service):
__tns__ = 'spyne.examples.authentication'
#rpc(Mandatory.String, Mandatory.String, _returns=String,
_throws=AuthenticationError)
def authenticate(ctx, user_name, password):
password_hash = user_db.get(user_name, None)
if password_hash is None:
raise AuthenticationError(user_name)
if bcrypt.hashpw(password, password_hash) == password_hash:
session_id = (user_name,
'%x' % random.randint(1 << 124, (1 << 128) - 1))
session_db.add(session_id)
else:
raise AuthenticationError(user_name)
return session_id[1]
class UserService(Service):
__tns__ = 'spyne.examples.authentication'
__in_header__ = RequestHeader
#rpc(Mandatory.String, _throws=PublicKeyError, _returns=Preferences)
def get_preferences(ctx, user_name):
if user_name == 'smith':
raise AuthorizationError()
retval = preferences_db[user_name]
return retval
def _on_method_call(ctx):
if ctx.in_object is None:
raise ArgumentError("RequestHeader is null")
if not (ctx.in_header.user_name, ctx.in_header.session_id) in session_db:
raise AuthenticationError(ctx.in_object.user_name)
UserService.event_manager.add_listener('method_call', _on_method_call)
if __name__ == '__main__':
from spyne.util.wsgi_wrapper import run_twisted
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
logging.getLogger('twisted').setLevel(logging.DEBUG)
application = Application([AuthenticationService, UserService],
tns='spyne.examples.authentication',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11()
)
twisted_apps = [
(WsgiApplication(application), 'app'),
]
sys.exit(run_twisted(twisted_apps, 8000))
When I run this code it gives me access to files from my soap service directory. But my functions for server doesn't work anymore.
How can I configure the soap service to ask for a login and password and then give me access to the server and all the functions?

How to mock a method of a dependent class?

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")

How to import a variable from a Pytest funtion to another Python script file?

I have the below Pytest script Webtest.py where my url to be tested is defined.
from seleniumwire import webdriver
import pytest
from selenium.webdriver.chrome.options import Options
class Test_main():
#pytest.fixture()
def test_setup(self):
# initiating browser
chrome_options = Options()
chrome_options.add_argument('--start-maximized')
chrome_options.add_argument('--headless')
self.driver = webdriver.Chrome(executable_path=r"drivers/chromedriver v86/chromedriver.exe",options=chrome_options)
yield
self.driver.close()
self.driver.quit()
print("Test Completed")
def test_case01(self,test_setup):
self.url='lifesciences.cactusglobal.com'
self.driver.get(self.url)
title=self.driver.title
print(title)
I want to use the self.url value from the above script to be used in another python script SSL_trial.py. I tried it like below, but was showing error even before execution.
from OpenSSL import SSL
from cryptography import x509
from cryptography.x509.oid import NameOID
import idna
from Test_Website_Security import Test_main #imported the file which is saved in the same folder
url_here= Test_main.test_case02().url #I tried to call the variable here
from socket import socket
from collections import namedtuple
HostInfo = namedtuple(field_names='cert hostname peername', typename='HostInfo')
HOSTS = [
(url_here, 443)
]
def verify_cert(cert, hostname):
cert.has_expired()
def get_certificate(hostname, port):
hostname_idna = idna.encode(hostname)
sock = socket()
sock.connect((hostname, port))
peername = sock.getpeername()
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.check_hostname = False
ctx.verify_mode = SSL.VERIFY_NONE
sock_ssl = SSL.Connection(ctx, sock)
sock_ssl.set_connect_state()
sock_ssl.set_tlsext_host_name(hostname_idna)
sock_ssl.do_handshake()
cert = sock_ssl.get_peer_certificate()
crypto_cert = cert.to_cryptography()
sock_ssl.close()
sock.close()
return HostInfo(cert=crypto_cert, peername=peername, hostname=hostname)
def get_alt_names(cert):
try:
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
return ext.value.get_values_for_type(x509.DNSName)
except x509.ExtensionNotFound:
return None
def get_common_name(cert):
try:
names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
return names[0].value
except x509.ExtensionNotFound:
return None
def get_issuer(cert):
try:
names = cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)
return names[0].value
except x509.ExtensionNotFound:
return None
def print_basic_info(hostinfo):
s = '''» {hostname} « … {peername}
\tcommonName: {commonname}
\tSAN: {SAN}
\tissuer: {issuer}
\tnotBefore: {notbefore}
\tnotAfter: {notafter}
'''.format(
hostname=hostinfo.hostname,
peername=hostinfo.peername,
commonname=get_common_name(hostinfo.cert),
SAN=get_alt_names(hostinfo.cert),
issuer=get_issuer(hostinfo.cert),
notbefore=hostinfo.cert.not_valid_before,
notafter=hostinfo.cert.not_valid_after
)
print(s)
print(type(s))
xyz=list(s.split("\t"))
print(xyz)
print(len(xyz))
print(xyz[5])
print(xyz[4])
def check_it_out(hostname, port):
hostinfo = get_certificate(hostname, port)
print_basic_info(hostinfo)
import concurrent.futures
if __name__ == '__main__':
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
for hostinfo in e.map(lambda x: get_certificate(x[0], x[1]), HOSTS):
print_basic_info(hostinfo)
I am not sure how to do it. I know its quite basic but any help is much appreciated.
if test_case01 somehow can retun self.url then probaly you can try the below solution :-
let's say A.py looks something like this :-
def test_case01():
url = 'lifesciences.cactusglobal.com'
return url
and if you want to use url from test_case01 into B.py :-
you can probably do something like this :
from A import *
var = test_case01()
print(var)

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?

Stomp Consumer using deferred.inlinecallback

I am implementing stomp consumer as a library. By calling this library in other application i should be able to get the data in ActiveMQ. I am implementing it as below, but I have a problem in returning the frame.body. I am not able to retrieve the data from outside the class.
from twisted.internet import defer
from stompest.async import Stomp
from stompest.async.listener import SubscriptionListener
from stompest.config import StompConfig
from socket import gethostname
from uuid import uuid1
import json
class Consumer(object):
def __init__(self, amq_uri):
self.amq_uri = amq_uri
self.hostname = gethostname()
self.config = StompConfig(uri=self.amq_uri)
#defer.inlineCallbacks
def run(self, in_queue):
client = yield Stomp(self.config)
headers = {
StompSpec.ACK_HEADER: StompSpec.ACK_CLIENT_INDIVIDUAL,
StompSpec.ID_HEADER: self.hostname,
'activemq.prefetchSize': '1000',
}
yield client.connect(headers=self._return_client_id())
client.subscribe(
in_queue,
headers,
listener=SubscriptionListener(self.consume)
)
try:
client = yield client.disconnected
except StompConnectionError:
yield client.connect(headers=self._return_client_id())
client.subscribe(
in_queue,
headers,
listener=SubscriptionListener(self.consume)
)
while True:
try:
yield client.disconnected
except StompProtocolError:
pass
except StompConnectionError:
yield client.connect(headers=self._return_client_id())
client.subscribe(
in_queue,
headers,
listener=SubscriptionListener(self.consume)
)
def _return_client_id(self):
client_id = {}
client_id['client-id'] = gethostname() + '-' + str(uuid1())
return client_id
def consume(self, client, frame):
data = json.loads(frame.body)
print 'Received Message Type {}'.format(type(data))
print 'Received Message {}'.format(data)
## I want to return data here. I am able to print the frame.body here.
# Call from another application
import Queue
from twisted.internet import reactor
amq_uri = 'tcp://localhost:61613'
in_queue = '/queue/test_queue'
c = Consumer(amq_uri)
c.run(in_queue)
print "data is from outside function", data # Should be able to get the data which is returned by consume here
reactor.run()
Can someone please let me know how can i achieve this.
Thanks
I found a solution to my problem. Instead of using async stomp library, i used sync stomp library. Implemented it as below,
class Consumer(object):
def __init__(self, amq_uri):
self.amq_uri = amq_uri
self.hostname = gethostname()
self.config = StompConfig(uri=self.amq_uri)
def run(self, in_queue, return_dict):
client = Stomp(self.config)
headers = {
StompSpec.ACK_HEADER: StompSpec.ACK_CLIENT_INDIVIDUAL,
StompSpec.ID_HEADER: self.hostname
}
client.connect()
client.subscribe(in_queue, headers)
try:
frame = client.receiveFrame()
data = json.dumps(frame.body)
except Exception as exc:
print exc
client.ack(frame)
client.disconnect()
return_dict['data'] = data
return data

Categories

Resources