I have successfully mocked boto3 client to test the positive case but unable to mock the same client for exceptions here is the code that i tried to mock the get_secret_value method of boto3 client to raise a ClientError.
class SecretsManager:
def __init__(self):
self.session = boto3.session.Session()
self.client = self.session.client(service_name='secretsmanager', region_name='region-name')
self.get_secrets()
def get_secrets(self): # no-pragma
try:
get_secret_value_response = self.client.get_secret_value(
SecretId=self.secret_name)
except ClientError as e:
if e.response['Error']['Code'] == 'DecryptionFailureException':
raise e
else:
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
self.secrets_dict = json.loads(secret)
else:
decoded_binary_secret = base64.b64decode(
get_secret_value_response['SecretBinary'])
Tests:
class TestSecretsManager:
#mock.patch("boto3.session.Session")
def test_get_secrets(self, mock_session):
# Arrange
mock_session_object = mock.Mock()
mock_client = mock.Mock()
mock_client.get_secret_value.return_value = {"SecretString": '{"my-secret1": "val1"}'}
mock_session_object.client.return_value = mock_client
mock_session.return_value = mock_session_object
secrets = SecretsManager()
assert secrets is not None
assert len(secrets.secrets_dict) == 1
assert secrets.secrets_dict['my-secret1'] == 'val1'
#mock.patch("boto3.session.Session")
def test_get_secrets_exception(self, mock_session):
# Arrange
mock_session.client.get_secret_value.side_effect = ClientError(error_response={"Error": {"Code": "DecryptionFailureException"}}, operation_name='Test')
with pytest.raises(Exception) as err:
secrets = SecretsManager()
assert err['Error']['Code'] == 'DecryptionFailureException'
test 1 is passing but, test 2 is not raising ClientError. Can somebody help me where am going wrong?
Related
Im trying to connect to my Tello drone with Spyder by socket but the dron dosen't send an answer back. It prints that the Tello drone refuses to enter command mode.
import socket
import threading
import time
import traceback
class Tello:
self.abort_flag = False
self.command_timeout = command_timeout
self.imperial = imperial
self.response = None
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.tello_address = (tello_ip, tello_port)
self.socket.bind((local_ip, local_port))
self.receive_thread = threading.Thread(target=self._receive_thread)
self.receive_thread.daemon=True
self.receive_thread.start()
if self.send_command('command') != 'OK':
raise RuntimeError('Tello rejected attempt to enter command mode')
def __del__(self):
self.socket.close()
def _receive_thread(self):
while True:
try:
self.response, ip = self.socket.recvfrom(256)
except Exception:
break
def send_command(self, command):
self.abort_flag = False
timer = threading.Timer(self.command_timeout, self.set_abort_flag)
self.socket.sendto(command.encode('utf-8'), self.tello_address)
timer.start()
while self.response is None:
if self.abort_flag is True:
raise RuntimeError('No response to command')
timer.cancel()
response = self.response.decode('utf-8')
self.response = None
return response
def set_abort_flag(self):
"""Sets self.abort_flag to True.
Used by the timer in Tello.send_command() to indicate to that a response
timeout has occurred.
"""
self.abort_flag = True
I have trouble using Flask socketio. Here is the code I use:
import json
import time
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from engineio.async_drivers import gevent
from flask_cors import CORS
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, cors_allowed_origins="*")
import queue
queue_notification_thread = queue.Queue()
def callback_notification(data):
print("callback device {}".format(data))
notification_thread = threading.Thread(target=notification_job, args=(data))
notification_thread.start()
queue_notification_thread.put(notification_thread)
def notification_job(data):
print("callback device in notification job {}".format(data))
socketio.emit("notification", data, broadcast=True)
#socketio.on('request')
def handle_message(data):
Logger.instance().debug('received message: {}'.format(data))
try:
if data.__contains__('data'):
response_message = dict()
response_message['Devices'] = dict()
response_message['Devices']['event'] = 'MY_EVENT'
socketio.emit('notification', response_message, broadcast=True)
else:
Logger.instance().error('Can\'t parse data {}'.format(data))
except OSError as err:
print('Error: when process {} \n ValueError {}'.format(data, err))
#socketio.on_error_default # handles all namespaces without an explicit error handler
def default_error_handler(e):
print('An error occured:')
print(e)
if __name__ == '__main__':
serialReader = SerialReader()
serialReader.start_reading(callback_notification)
http_server = WSGIServer(('', 5000), app, handler_class=WebSocketHandler)
http_server.serve_forever()
And the reader with call asynchronisly:
class SerialController:
serial_port: str
serial_device: serial.Serial
reading_thread: threading.Thread
device_name: str
def __init__(self, serial_port: str = "/dev/ttyACM0", baudrate=115200, read_timeout=0.2, device_name=''):
self.serial_port = serial_port
self.device_name = device_name
self.serial_device = serial.Serial(port=self.serial_port, baudrate=baudrate, timeout=0.2)
def start_reading(self, callback_function):
self.reading_callback = callback_function
# run the thread to
self.reading_thread = threading.Thread(target=self.read_job)
self.reading_thread.start()
def read_job(self):
available_data = 0
while True:
if self.serial_device.in_waiting > available_data:
available_data = self.serial_device.in_waiting
print('available_data {}'.format(available_data))
time.sleep(0.1)
else:
if available_data != 0:
data = self.serial_device.readall()
available_data = 0
if data != b'' and data != b'\n':
if self.reading_callback != None:
message = dict()
message["Reader"] = dict()
message["Reader"]["device"] = self.device_name
message["Reader"]["data"] = data
self.reading_callback(message)
time.sleep(1)
When I receive a message in #socketio.on('request') the bradcast emission work properly with no delay. When I use callback_notification called from my serial reader the breadcast emission have variable delay ( from 1seconde to 10 secondes).
On my server the message "callback device ..." is printed instantly but the client receive the message after few second.
I tried to the emission call in a thread like in the shown code but there is no improvment
I am new to python and AWS lambda. I am trying to run this script from the lambda function but I am getting error:
Runtime.HandlerNotFound
This script is working fine if I run it from the ec2 instance, but when I run the same script from AWS lambda it throws an error.
I would be really thankful if someone guides me on what I did wrong.
Thank you
import boto3
import requests
import time
AWS_Access_Key_ID =
AWS_Secret_Access_Key =
DELAY_TIME=10 # 10 Seconds
region = 'us-east-2'
# instances = ['']
instances = {
'instance id': 'http://link',
'instance id': 'http://link'
}
ec2 = None
try:
ec2 = boto3.client('ec2', aws_access_key_id=AWS_Access_Key_ID, aws_secret_access_key=AWS_Secret_Access_Key, region_name=region)
# ec2 = boto3.resource('ec2',aws_access_key_id=AWS_Access_Key_ID, aws_secret_access_key=AWS_Secret_Access_Key, region_name=region)
except Exception as e:
print(e)
print("AWS CREDS ERROR, Exiting...")
exit()
def startInstances(instancesIds):
if(type(instancesIds) != list):
instancesIds = [instancesIds]
try:
response = ec2.start_instances(InstanceIds=instancesIds, DryRun=False)
print(response)
print("Instances Started")
except ClientError as e:
print(e)
print("Instances Failed to Start")
def stopInstances(instancesIds):
if(type(instancesIds) != list):
instancesIds = [instancesIds
]
try:
response = ec2.stop_instances(InstanceIds=instancesIds, DryRun=False)
print(response)
print("Instances Stopped")
except ClientError as e:
print(e)
print("Instances Failed to Stop")
def check():
for x in instances:
retry = 0
live = False
print("Checking Webiste " + instances[x])
while(retry < 5):
try:
r = requests.get(instances[x] ,verify=True)
if(r.status_code == 200):
live = True
break
except:
print("Not Live, retry time " + str(retry + 1))
print("Delaying request for " + str(DELAY_TIME) + " seconds...")
retry += 1
time.sleep(DELAY_TIME)
if(live):
print("Website is live")
# call function to start the ec2 instance
startInstances(x)
else:
# call function to stop the ec2 instance
print('Website is dead')
stopInstances(x)
print("")
def main():
check()
if __name__ == '__main__':
main()
https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html You need to specify what is the name of the handler function, which is the function that AWS lambda will call. Then you need to implement that function in your Python script.
I had a similar problem recently. I was able to define a lambda handler function in my python code that solved the problem. Got the guidance from this post
in short, add this code (adjust naming conventions accordingly):
import botocore
import boto3
def lambda_handler(event, context):
s3 = boto3.resource('s3')
bucket = s3.Bucket('bucketname')
exists = True
try:
s3.meta.client.head_bucket(Bucket='bucketname')
except botocore.exceptions.ClientError as e:
# If a client error is thrown, then check that it was a 404 error.
# If it was a 404 error, then the bucket does not exist.
error_code = int(e.response['Error']['Code'])
if error_code == 404:
exists = False
class Kill_Tasks:
def __init__(self):
self.action = None
self.cmd=[]
def kill_tasks(self):
self.action = False
#exp task = chrome.exe
target = 'chrome.exe'
try:
self.cmd = suprocess.run(['taskkill','/im', target,'/f'],capture_output=True, check=True)
self.action = True
except subprocess.CalledProcessError as e:
print(e)
self.action = False
return self.cmd
Unit Test for a function throws an exception in Python.
The question is: how can I do the Unit Testing for the exception part. I'm doing Code Coverage for this function. But I am not able to check the exception because it's always True. Do I need to do a fake connection to be able to test the exception? Please help me out! Thanks
You should use unittest.mock.Mock.side_effect to mock a raised exception.
E.g.
kill_tasks.py:
import subprocess
class Kill_Tasks:
def __init__(self):
self.action = None
self.cmd = []
def kill_tasks(self):
self.action = False
# exp task = chrome.exe
target = 'chrome.exe'
try:
self.cmd = subprocess.run(['taskkill', '/im', target, '/f'], capture_output=True, check=True)
self.action = True
except subprocess.CalledProcessError as e:
print(e)
self.action = False
return self.cmd
test_kill_tasks.py:
import unittest
from unittest.mock import patch
from subprocess import CompletedProcess, CalledProcessError
from kill_tasks import Kill_Tasks
class TestKillTasks(unittest.TestCase):
#patch('kill_tasks.subprocess.run')
def test_kill_tasks_success(self, mock_run):
mock_run.return_value = CompletedProcess(args=['taskkill', '/im', 'chrome.exe', '/f'], returncode=0)
kill_tasks_instance = Kill_Tasks()
cmd = kill_tasks_instance.kill_tasks()
mock_run.assert_called_once_with(['taskkill', '/im', 'chrome.exe', '/f'], capture_output=True, check=True)
self.assertIsInstance(cmd, CompletedProcess)
self.assertTrue(kill_tasks_instance.action)
#patch('kill_tasks.subprocess.run')
def test_kill_tasks_exception(self, mock_run):
mock_run.side_effect = CalledProcessError(returncode=1, cmd='123')
kill_tasks_instance = Kill_Tasks()
kill_tasks_instance.kill_tasks()
self.assertFalse(kill_tasks_instance.action)
if __name__ == '__main__':
unittest.main()
unit test result:
coverage run /Users/dulin/workspace/github.com/mrdulin/python-codelab/src/stackoverflow/66848842/test_kill_tasks.py && coverage report -m --include='./src/**'
Command '123' returned non-zero exit status 1.
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Name Stmts Miss Cover Missing
-----------------------------------------------------------------------------
src/stackoverflow/66848842/kill_tasks.py 15 0 100%
src/stackoverflow/66848842/test_kill_tasks.py 21 0 100%
-----------------------------------------------------------------------------
TOTAL 36 0 100%
How can we capture the print statement within an except when the exception is mocked?
In the example below, I am mocking the make_request_and_get_response using side_effect.
Then, using the pytest.raises I assert that this exception was raised. But, when trying to assert that the corresponding print statement is being output by using capsys to capture it, nothing is captured.
Am I correct to expect that since I am mocking the exception the corresponding print statement will be executed?
import pytest
from unittest import mock
from foo.main import get_response_from_external_api
def test_url_error(capsys):
mock_query = mock.Mock()
mock_query.make_request_and_get_response.side_effect = Exception('HTTPError')
with pytest.raises(Exception) as excinfo:
get_response_from_external_api(mock_query)
assert str(excinfo.value) == 'HTTPError '
out, err = capsys.readouterr()
assert "Got a HTTPError" in out # AssertionError
query.py
from foo.query import *
def get_response_from_external_api(query):
response = 0
try:
response = query.make_request_and_get_response()
except urllib.error.HTTPError as e:
print('Got a HTTPError: ', e)
return response
if __name__ == "__main__":
query = Query('input A', 'input B')
result = get_response_from_external_api(query)
return result
main.py
from foo.query import *
def get_response_from_external_api(query):
response = 0
try:
response = query.make_request_and_get_response()
except urllib.error.HTTPError as e:
print('Got a HTTPError: ', e)
return response
if __name__ == "__main__":
query = Query('input A', 'input B')
result = get_response_from_external_api(query)
return resul