Pytest-cov with Moto in list_stacks() from Boto3 - python-3.x

I'm trying to create a test with pytest and moto that check if the StackStatus from the dictionary returned from the function list_stacks() (https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.list_stacks) is DELETE_COMPLETE.
I create a pytest.fixture for the client connection:
#pytest.fixture(scope='function')
def cf(aws_credentials):
with mock_cloudformation():
yield boto3.client('cloudformation')
And I created a dummie template:
#pytest.fixture(scope='function')
def template_body_data():
'The Cloud Formation template'
template_data = {
'Resources': {'MyS3Bucket': {'Type': 'AWS::S3::Bucket', 'Properties': {}}}
}
return template_data
And in my test function I create the stack and after I deleted:
#mock_cloudformation
def test_deleted_stack(cf, template_body_data):
params = {'StackName': 'teste', 'TemplateBody': yaml.dump(template_body_data)}
cf.create_stack(**params)
cf.delete_stack(StackName='teste')
assert check_stack_exists('teste') is False
The function that is being testes is this one:
def check_stack_exists(stack_name):
cf = get_client()
list_stack = cf.list_stacks()['StackSummaries']
for stack in list_stack:
if stack['StackStatus'] == 'DELETE_COMPLETE':
continue
if stack['StackName'] == stack_name:
return True
return False
I'm facing a issue:
It complains that my template doesn't have a BucketName:
___________________ test_check_stack_exists_deleted_stack ___________________
cf = <botocore.client.CloudFormation object at 0x7f59ca68f580>
template_body_data = {'Resources': {'MyS3Bucket': {'Properties': {}, 'Type': 'AWS::S3::Bucket'}}}
#mock_cloudformation
def test_check_stack_exists_deleted_stack(cf, template_body_data):
'Test the stack search if the stack exists'
params = {'StackName': 'teste', 'TemplateBody': yaml.dump(template_body_data)}
cf.create_stack(**params)
> cf.delete_stack(StackName='teste')
tests/test_cl_uploader.py:87:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/client.py:316: in _api_call
return self._make_api_call(operation_name, kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/client.py:621: in _make_api_call
http, parsed_response = self._make_request(
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/client.py:641: in _make_request
return self._endpoint.make_request(operation_model, request_dict)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/endpoint.py:102: in make_request
return self._send_request(request_dict, operation_model)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/endpoint.py:136: in _send_request
while self._needs_retry(attempts, operation_model, request_dict,
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/endpoint.py:253: in _needs_retry
responses = self._event_emitter.emit(
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/hooks.py:356: in emit
return self._emitter.emit(aliased_event_name, **kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/hooks.py:228: in emit
return self._emit(event_name, kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/hooks.py:211: in _emit
response = handler(**kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/retryhandler.py:183: in __call__
if self._checker(attempts, response, caught_exception):
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/retryhandler.py:250: in __call__
should_retry = self._should_retry(attempt_number, response,
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/retryhandler.py:269: in _should_retry
return self._checker(attempt_number, response, caught_exception)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/retryhandler.py:316: in __call__
checker_response = checker(attempt_number, response,
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/retryhandler.py:222: in __call__
return self._check_caught_exception(
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/retryhandler.py:359: in _check_caught_exception
raise caught_exception
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/endpoint.py:197: in _do_get_response
responses = self._event_emitter.emit(event_name, request=request)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/hooks.py:356: in emit
return self._emitter.emit(aliased_event_name, **kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/hooks.py:228: in emit
return self._emit(event_name, kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/hooks.py:211: in _emit
response = handler(**kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/core/models.py:322: in __call__
status, headers, body = response_callback(
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/core/responses.py:202: in dispatch
return cls()._dispatch(*args, **kwargs)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/core/responses.py:312: in _dispatch
return self.call_action()
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/core/responses.py:397: in call_action
response = method()
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/cloudformation/responses.py:380: in delete_stack
self.cloudformation_backend.delete_stack(name_or_stack_id)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/cloudformation/models.py:734: in delete_stack
self.delete_stack(stack.stack_id)
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/cloudformation/models.py:726: in delete_stack
stack.delete()
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/cloudformation/models.py:363: in delete
self.resource_map.delete()
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/cloudformation/parsing.py:677: in delete
raise last_exception
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <moto.cloudformation.parsing.ResourceMap object at 0x7f59cabd3940>
def delete(self):
remaining_resources = set(self.resources)
tries = 1
while remaining_resources and tries < 5:
for resource in remaining_resources.copy():
parsed_resource = self._parsed_resources.get(resource)
try:
if parsed_resource and hasattr(parsed_resource, "delete"):
parsed_resource.delete(self._region_name)
else:
resource_name_attribute = (
parsed_resource.cloudformation_name_type()
if hasattr(parsed_resource, "cloudformation_name_type")
else resource_name_property_from_type(parsed_resource.type)
)
if resource_name_attribute:
resource_json = self._resource_json_map[
parsed_resource.logical_resource_id
]
> resource_name = resource_json["Properties"][
resource_name_attribute
]
E KeyError: 'BucketName'
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/moto/cloudformation/parsing.py:662: KeyError
But when I add one, it giver another error:
================================= FAILURES ==================================
___________________ test_check_stack_exists_deleted_stack ___________________
cf = <botocore.client.CloudFormation object at 0x7f7a02ca4ee0>
template_body_data = {'Resources': {'MyS3Bucket': {'Properties': {'BucketName': 'teste'}, 'Type': 'AWS::S3::Bucket'}}}
#mock_cloudformation
def test_check_stack_exists_deleted_stack(cf, template_body_data):
'Test the stack search if the stack exists'
params = {'StackName': 'teste', 'TemplateBody': yaml.dump(template_body_data)}
> cf.create_stack(**params)
tests/test_cl_uploader.py:86:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/client.py:316: in _api_call
return self._make_api_call(operation_name, kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <botocore.client.CloudFormation object at 0x7f7a02ca4ee0>
operation_name = 'CreateStack'
api_params = {'StackName': 'teste', 'TemplateBody': 'Resources:\n MyS3Bucket:\n Properties:\n BucketName: teste\n Type: AWS::S3::Bucket\n'}
def _make_api_call(self, operation_name, api_params):
operation_model = self._service_model.operation_model(operation_name)
service_name = self._service_model.service_name
history_recorder.record('API_CALL', {
'service': service_name,
'operation': operation_name,
'params': api_params,
})
if operation_model.deprecated:
logger.debug('Warning: %s.%s() is deprecated',
service_name, operation_name)
request_context = {
'client_region': self.meta.region_name,
'client_config': self.meta.config,
'has_streaming_input': operation_model.has_streaming_input,
'auth_type': operation_model.auth_type,
}
request_dict = self._convert_to_request_dict(
api_params, operation_model, context=request_context)
service_id = self._service_model.service_id.hyphenize()
handler, event_response = self.meta.events.emit_until_response(
'before-call.{service_id}.{operation_name}'.format(
service_id=service_id,
operation_name=operation_name),
model=operation_model, params=request_dict,
request_signer=self._request_signer, context=request_context)
if event_response is not None:
http, parsed_response = event_response
else:
http, parsed_response = self._make_request(
operation_model, request_dict, request_context)
self.meta.events.emit(
'after-call.{service_id}.{operation_name}'.format(
service_id=service_id,
operation_name=operation_name),
http_response=http, parsed=parsed_response,
model=operation_model, context=request_context
)
if http.status_code >= 300:
error_code = parsed_response.get("Error", {}).get("Code")
error_class = self.exceptions.from_code(error_code)
> raise error_class(parsed_response, operation_name)
E botocore.exceptions.ClientError: An error occurred (Unknown) when calling the CreateStack operation: Unknown
../../.cache/pypoetry/virtualenvs/cl-uploader-12nYBdPj-py3.8/lib/python3.8/site-packages/botocore/client.py:635: ClientError

Very late answer, but since you specified an S3 bucket type in the template, the cloudformation client will likely try making calls to S3 to check if the bucket resource exists. So we would also need to mock those s3 calls. You need to put a #mock_s3 annotation in that test method:
#mock_s3
#mock_cloudformation
def test_deleted_stack(cf, template_body_data):
params = {'StackName': 'teste', 'TemplateBody': yaml.dump(template_body_data)}
cf.create_stack(**params)
cf.delete_stack(StackName='teste')
assert check_stack_exists('teste') is False

Related

Pytest running unittest failed with Attribute error

I created below function and it's working fine but i am struggling with creating unittest for this function.
Here's my function:
def put_config_file_on_device(device, config_backup=None):
try:
runn_config = config_backup
runn_config_database = running_config_from_database(device)
if runn_config:
raw_config = runn_config
else:
raw_config = runn_config_database
new_file, filename = tempfile.mkstemp()
os.write(new_file, raw_config.encode('utf-8'))
ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.load_system_host_keys()
handler = get_handler(device)
passwd = handler._get_admin_passwords()[1][0]
ssh.connect(device, username='root', password=get_cred('root'),
look_for_keys=False)
vendor = str(handler)
if 'Juniper' in vendor:
remote_path = f'/var/tmp/{device}.cfg'
if 'Cisco' in vendor:
remote_path = f'harddisk:/{device}.cfg'
scp = SCPClient(ssh.get_transport())
file_path = f'{filename}'
scp.put(file_path, remote_path, recursive=False)
scp.close()
os.remove(filename)
return True, f"File store successfully on the device {remote_path}"
except Exception as err:
return False, f"Failed to store config on Exception: {err}"
I created below unittest but it's not working. I am getting error while tetsinh the UT.
i have mocked all the dependencies but not sure what's wrong with the code. I get Attribute error for <class 'paramiko.client.SSHClient.
#pytest.mark.parametrize('mock_config_backup', ['someconfig\r\n'])
#patch("test.scripts.script.get_cred")
#patch("test.scripts.script.os")
#patch("test.scripts.script.SSHClient.")
#patch("test.scripts.script.SCPClient")
#patch("test.scripts.script.tempfile.mkstemp")
#patch("test.scripts.script.get_device_handler")
def test_put_config_file_on_device(mock_handler, mock_temp, mock_os, mock_ssh, mock_scp, mock_cred, mock_config_backup):
conf = 'someconfig\r\n'
mock_temp.return_value = new_file, filename
mock.os.write(new_file, conf.encode('utf-8'))
remote_path = '/var/tmp/devA.cfg'
file_path = f'{filename}'
mock_cred.return_value = 'xyz'
ret = MagicMock()
ret.connect('devA', username='root', password='xyz', look_for_keys=False)
mock_ssh.return_value = ret
mock_scp.put = (file_path, remote_path)
scp.close()
mock.os.remove(filename)
status, out = test.scripts.script.put_config_file_on_device(device, mock_config_backup)
assert status and out == 'File store successfully on the device /var/tmp/devA.cfg'
getting below error.
=============================================================================================== FAILURES ===============================================================================================
____________________________________________________________________________ test_put_config_file_on_device[someconfig\r\n] ____________________________________________________________________________
args = (), keywargs = {'mock_config_backup': 'someconfig\r\n'}
#wraps(func)
def patched(*args, **keywargs):
with self.decoration_helper(patched,
args,
> keywargs) as (newargs, newkeywargs):
/home/lib/python3.6/site-packages/mock/mock.py:1368:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.6/contextlib.py:81: in __enter__
return next(self.gen)
/home/lib/python3.6/site-packages/mock/mock.py:1334: in decoration_helper
arg = patching.__enter__()
/home/lib/python3.6/site-packages/mock/mock.py:1437: in __enter__
original, local = self.get_original()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <mock.mock._patch object at 0x7f0e60450240>
def get_original(self):
target = self.getter()
name = self.attribute
original = DEFAULT
local = False
try:
original = target.__dict__[name]
except (AttributeError, KeyError):
original = getattr(target, name, DEFAULT)
else:
local = True
if name in _builtins and isinstance(target, ModuleType):
self.create = True
if not self.create and original is DEFAULT:
raise AttributeError(
> "%s does not have the attribute %r" % (target, name)
)
E AttributeError: <class 'paramiko.client.SSHClient'> does not have the attribute ''
/home/lib/python3.6/site-packages/mock/mock.py:1411: AttributeError
===================================================================================== 1 failed, 6 passed in 5.67s

CLI arguments are not processed in conftest.py in my pytest

My project implementation is to capture details from CLI to determine the environment, to generate the token in that environment and to return the token and the application URL.
Here is the code in conftest.py file
def pytest_addoption(parser):
parser.addoption('--env',
dest='testenv',
choices=["qa","aws","prod"],
default='qa',
help='Specify environment: "qa", "aws", "prod".')
#pytest.fixture(scope='session')
#def conftest_setup(request):
# env = request.config.getoption("--env")
def conftest_setup(request):
env = request.config.getoption("--env")
print(env)
if (env =='aws'):
url='AWSURL'
elif ( env =='prod'):
url='prodURL'
else:
url='QAURL'
token = requests.post(auth=HTTPBasicAuth(clientID, secret),headers=tokenheaders, data=payload)
auth = token.json()['access_token']
return auth,url
test_service.py has
auth1=''
url1=''
def initialCall(conftest_setup):
auth1=conftest_setup[1] # Pretty sure this is wrong, but couldnt get a way to retrieve this
url1=conftest_setup[2]
# Now I want to use the auth1 and url1 obtained from above method to the below method
def response():
print("auth is " ,auth1)
print("URL is " ,url1)
headers = {'content-type': 'application/json',
'Authorization' : auth1}
response=
(requests.post(url1,data=json.dumps(data1),headers=headers)).json()
Currently, I get this exception
request = <FixtureRequest for <Function
> ???
test\test_service_1.py:41:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\software\python\lib\site-packages\pytest_bdd\scenario.py:195: in _execute_scenario
_execute_step_function(request, scenario, step, step_func)
..\..\software\python\lib\site-packages\pytest_bdd\scenario.py:136: in _execute_step_function
step_func(**kwargs)
..\..\software\python\lib\site-packages\pytest_bdd\steps.py:164: in step_func
result = request.getfixturevalue(func.__name__)
..\..\software\python\lib\site-packages\_pytest\fixtures.py:478: in getfixturevalue
return self._get_active_fixturedef(argname).cached_result[0]
..\..\software\python\lib\site-packages\_pytest\fixtures.py:501: in _get_active_fixturedef
self._compute_fixture_value(fixturedef)
..\..\software\python\lib\site-packages\_pytest\fixtures.py:586: in _compute_fixture_value
fixturedef.execute(request=subrequest)
..\..\software\python\lib\site-packages\_pytest\fixtures.py:895: in execute
return hook.pytest_fixture_setup(fixturedef=self, request=request)
..\..\software\python\lib\site-packages\pluggy\hooks.py:289: in __call__
return self._hookexec(self, self.get_hookimpls(), kwargs)
..\..\software\python\lib\site-packages\pluggy\manager.py:68: in _hookexec
return self._inner_hookexec(hook, methods, kwargs)
..\..\software\python\lib\site-packages\pluggy\manager.py:62: in <lambda>
firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
..\..\software\python\lib\site-packages\_pytest\fixtures.py:937: in pytest_fixture_setup
result = call_fixture_func(fixturefunc, request, kwargs)
..\..\software\python\lib\site-packages\_pytest\fixtures.py:794: in call_fixture_func
res = fixturefunc(**kwargs)
***test\test_service_1.py:40: in testws_response
response=(requests.post(url1,data=json.dumps(data1),headers=headers)).json()***
..\..\software\python\lib\site-packages\requests\api.py:116: in post
return request('post', url, data=data, json=json, **kwargs)
..\..\software\python\lib\site-packages\requests\api.py:60: in request
return session.request(method=method, url=url, **kwargs)
..\..\software\python\lib\site-packages\requests\sessions.py:519: in request
prep = self.prepare_request(req)
..\..\software\python\lib\site-packages\requests\sessions.py:462: in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
..\..\software\python\lib\site-packages\requests\models.py:313: in prepare
self.prepare_url(url, params)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <PreparedRequest [POST]>, url = '', params = OrderedDict()
def prepare_url(self, url, params):
"""Prepares the given HTTP URL."""
#: Accept objects that have string representations.
#: We're unable to blindly call unicode/str functions
#: as this will include the bytestring indicator (b'')
#: on python 3.x.
#: https://github.com/requests/requests/pull/2238
if isinstance(url, bytes):
url = url.decode('utf8')
else:
url = unicode(url) if is_py2 else str(url)
# Remove leading whitespaces from url
url = url.lstrip()
# Don't do any URL preparation for non-HTTP schemes like `mailto`,
# `data` etc to work around exceptions from `url_parse`, which
# handles RFC 3986 only.
if ':' in url and not url.lower().startswith('http'):
self.url = url
return
# Support for unicode domain names and paths.
try:
scheme, auth, host, port, path, query, fragment = parse_url(url)
except LocationParseError as e:
raise InvalidURL(*e.args)
if not scheme:
error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?")
error = error.format(to_native_string(url, 'utf8'))
> raise MissingSchema(error)
E requests.exceptions.MissingSchema: Invalid URL '': No schema supplied. Perhaps you meant http://?
..\..\software\python\lib\site-packages\requests\models.py:387: MissingSchema
No console output observed for print(env) in conftest.py file while
auth is None
URL IS None
are observed for print statements in test_service
CLI execution throws error when I pass pytest -s --env='qa' but is acceptable for pytest -s --env=qa. So the CLI arguments are captured.
Please help me to capture the auth and url from conftest.py across the test file.
Also, is there a way I can use this auth,url returned from conftest.py to use across multiple test files?
Many thanks to #hoefling for providing valuable insights.
I've added/updated the fixture in the following places and declared auth1 and url1 as global inside initialcall method. It now works as expected.
test_service.py has
#pytest.fixture(autouse=True, scope='session')
def initialCall(conftest_setup):
global auth1,AN_API1
auth1 = conftest_setup[0]
AN_API1=conftest_setup[1]
conftest.py has
#pytest.fixture(autouse=True,scope='session')
def conftest_setup(request):

MissingSchema error while test Django via Pytest and Requests

Try to make simple test admin page via python-requests
import requests
from django.urls import reverse
def test_admin():
resp = requests.get(reverse('admin:index'))
assert resp.status_code == 200
but got unexpected error
def test_admin():
> resp = requests.get(reverse('admin:index'))
src/users/tests/test_user_admin.py:6:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv1/lib/python3.5/site-packages/requests/api.py:72: in get
return request('get', url, params=params, **kwargs)
venv1/lib/python3.5/site-packages/requests/api.py:58: in request
return session.request(method=method, url=url, **kwargs)
venv1/lib/python3.5/site-packages/requests/sessions.py:494: in request
prep = self.prepare_request(req)
venv1/lib/python3.5/site-packages/requests/sessions.py:437: in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
venv1/lib/python3.5/site-packages/requests/models.py:305: in prepare
self.prepare_url(url, params)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> raise MissingSchema(error)
E requests.exceptions.MissingSchema: Invalid URL '/admin/': No schema supplied. Perhaps you meant http:///admin/?
venv1/lib/python3.5/site-packages/requests/models.py:379: MissingSchema
List of installed packages
pytest==3.3.2
requests==2.18.4
pytest-django==3.1.2
django==1.10.2
Read a lot of docs and manual but can't find solution.
Test with different pytest.ini, manage.py, wsgi.py and settings.py
Simple tests assert 200==200 work properly.
Where is the problem?
Use request.build_absolute_uri():
resp = requests.get(request.build_absolute_uri(reverse('admin:index')))
As the error suggests, requests requires a url with schema (http, https, ftp etc).
The problem is that reverse returns a relative path (eg /admin/index) but requests needs a full url including a schema (eg http://localhost/admin/index).
If you insist on using reverse('admin:index') rather than hardcoding the full url in the test, you will need to prepend http://localhost:<port>/ (or whatever is the correct host in your case) to whatever reverse('admin:index') returns.

pytest argparse test case does not work

I'm using Python 3.4.6.
Here is the factory:
def create_parser():
""" Create argument parser """
# Input configuration parameters
_parser = argparse.ArgumentParser(description='Segments Engine')
# Application only parameters
_parser.add_argument(
'-V', '--version', version='%(prog)s ' + __version__,
action='version')
_pasrser.add_argument(
'-d', '--debug', action='store_true')
return _parser
The test case is as follows:
class TestCli(unittest.TestCase):
""" Test the cli.py module """
def __init__(self, *args, **kwargs):
""" Initialize Unit Test """
super(TestCli, self).__init__(*args, **kwargs)
def setup(self):
""" Setup before each test case """
setup_config(app)
setup_logging(app)
def teardown(self):
""" Tear down after each test case """
pass
def test_version(self):
parser = create_parser()
with pytest.raises(SystemExit):
args = vars(parser.parse_args(['', '-V']))
assert args.version == __version__
def test_debug(self):
parser = create_parser()
args = parser.parse_args(['-d'])
assert args.debug
Then I execute as follows:
pytest tests/test_cli.py
I get the following errors, what am I doing wrong?
======================================================================================= test session starts =========================================================================================
platform darwin -- Python 3.4.6, pytest-3.1.3, py-1.4.34, pluggy-0.4.0
rootdir: /Users/francisco.benavides/Documents/src/segments-engine, inifile:
collected 1 item s
tests/test_cli.py F
self = <tests.test_cli.TestCli testMethod=test_version>
def test_version(self):
parser = create_parser()
> args = vars(parser.parse_args(['', '-V']))
tests/test_cli.py:29:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:1728: in parse_args
args, argv = self.parse_known_args(args, namespace)
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:1760: in parse_known_args
namespace, args = self._parse_known_args(args, namespace)
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:1966: in _parse_known_args
start_index = consume_optional(start_index)
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:1906: in consume_optional
take_action(action, args, option_string)
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:1834: in take_action
action(self, namespace, argument_values, option_string)
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:1043: in __call__
parser.exit()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = ArgumentParser(prog='pytest', usage=None, description='Segments Engine', formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True), status = 0, message = None
def exit(self, status=0, message=None):
if message:
self._print_message(message, _sys.stderr)
> _sys.exit(status)
E SystemExit: 0
../../../.pyenv/versions/3.4.6/lib/python3.4/argparse.py:2373: SystemExit
---------------------------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------------------------
pytest 0.0.1
I have tried several ways I found googling, but so far nothing has helped me.
Just a guess since you haven't shown us your create_parser implementation:
-V is a "version" type command (?), if you read the docs version is to
"[print] version information and [exit] when invoked".
You'll want to capture the exit, here's one way:
with pytest.raises(SystemExit):
parser.parse_args(['', '-V'])
# maybe use `capsys` to verify that the version was printed
out, err = capsys.readouterr()
# ...

A syntax error when passing a NumPy array to a function created with lambdify in SymPy

I am trying to use pytest on a function which takes as input a string and it parses it with lambdify.
from sympy import sympify, symbols, lambdify
import numpy as np
def func(x):
a = np.arange(10)
expr = 'sin(x)'
f = lambdify(x, sympify(expr), 'numpy')
return f(a)
def test_answer():
thelist = np.array([
0., 0.84147098, 0.90929743, 0.14112001, -0.7568025, -0.95892427,
-0.2794155, 0.6569866, 0.98935825, 0.41211849])
assert func(np.arange(10)) == thelist
if __name__ == "__main__":
x = symbols('x')
func(x)
I am receiving :
assert func(np.arange(10)) == thelist
test_sample.py:23: (it is the `assert func()`)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test_sample.py:13: in func (it is the `f = lambdify(x, sympify(expr), 'numpy')`)
f = lambdify(x, sympify(expr), 'numpy')
...
func = eval(lstr, namespace)
E File "<string>", line 1
E lambda 0,1,2,3,4,5,6,7,8,9: (sin(x))
E ^
E SyntaxError: invalid syntax
../anaconda2/envs/pytest/lib/python3.6/site-packages/sympy/utilities/lambdify.py:387: SyntaxError
---- UPDATE ----------------
If I use this code:
from sympy import symbols, sin
from sympy.utilities.autowrap import ufuncify
import numpy as np
def func(x):
a = np.arange(10)
f = ufuncify(x, sin(x), backend='numpy')
return f(a)
def test_answer():
thelist = np.array([
0., 0.84147098, 0.90929743, 0.14112001, -0.7568025, -0.95892427,
-0.2794155, 0.6569866, 0.98935825, 0.41211849])
assert all(func(np.arange(10)) == thelist)
if __name__ == "__main__":
x = symbols('x')
func(x)
and run it with pytest, I am getting :
args = (sin, array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])), kwargs = {}
def wrapper(*args, **kwargs):
try:
> retval = cfunc(*args, **kwargs)
E TypeError: unhashable type: 'numpy.ndarray'
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/core/cache.py:93: TypeError
During handling of the above exception, another exception occurred:
args = (sin, array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])), kwargs = {}
def wrapper(*args, **kwargs):
try:
> retval = cfunc(*args, **kwargs)
E TypeError: unhashable type: 'numpy.ndarray'
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/core/cache.py:93: TypeError
During handling of the above exception, another exception occurred:
def test_answer():
thelist = np.array([
0., 0.84147098, 0.90929743, 0.14112001, -0.7568025, -0.95892427,
-0.2794155, 0.6569866, 0.98935825, 0.41211849])
> assert all(func(np.arange(10)) == thelist)
test_ufuncify.py:23:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test_ufuncify.py:14: in func
f = ufuncify(x, sin(x), backend='numpy')
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/core/cache.py:95: in wrapper
retval = func(*args, **kwargs)
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/core/function.py:385: in __new__
result = super(Function, cls).__new__(cls, *args, **options)
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/core/cache.py:95: in wrapper
retval = func(*args, **kwargs)
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/core/function.py:209: in __new__
evaluated = cls.eval(*args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls = sin, arg = array([], shape=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), dtype=float64)
#classmethod
def eval(cls, arg):
from sympy.calculus import AccumBounds
> if arg.is_Number:
E AttributeError: 'numpy.ndarray' object has no attribute 'is_Number'
../anaconda3/envs/parsing/lib/python3.6/site-packages/sympy/functions/elementary/trigonometric.py:228: AttributeError
In order to create a binary function that supports NumPy broadcasting, you need ufuncify, not lambdify. There are other issues with your code:
The assertion statement should be of the form all(array1 == array2)
You use x as a function parameter and also as a SymPy symbol - within the same function.
You have copied low-precision rounded values of sin(1), sin(2), ... which are not equal to the actual output of sine function.
You have not imported the sine function
Here is this code with the issues rectified:
from sympy import symbols, sin
from sympy.utilities.autowrap import ufuncify
import numpy as np
def test_answer():
thelist = np.array([0.0, 0.8414709848078965, 0.90929742682568171, 0.14112000805986721, -0.7568024953079282, -0.95892427466313845, -0.27941549819892586, 0.65698659871878906, 0.98935824662338179, 0.41211848524175659])
assert all(func(np.arange(10)) == thelist)
x = symbols('x')
func = ufuncify(x, sin(x), backend='numpy')
test_answer()
Remark
Use repr(a) to get a string that accurately represents a number a. The values printed with print are rounded. There is also set_printoptions

Resources