falcon, AttributeError: 'API' object has no attribute 'create' - python

I'm trying test my falcon routes, but tests always failed, and looks like I make all things right.
my app.py
import falcon
from resources.static import StaticResource
api = falcon.API()
api.add_route('/', StaticResource())
and my test directory tests/static.py
from falcon import testing
import pytest
from app import api
#pytest.fixture(scope='module')
def client():
# Assume the hypothetical `myapp` package has a
# function called `create()` to initialize and
# return a `falcon.API` instance.
return testing.TestClient(api.create())
def test_get_message(client):
result = client.simulate_get('/')
assert result.status_code == 200
Help please, why I got AttributeError: 'API' object has no attribute 'create'
error? Thanks.

You are missing the hypothetical create() function in your app.py.
Your app.py should look like the following:
import falcon
from resources.static import StaticResource
def create():
api = falcon.API()
api.add_route('/', StaticResource())
return api
api = create()
Then in your tests/static.py should look like:
from falcon import testing
import pytest
from app import create
#pytest.fixture(scope='module')
def client():
return testing.TestClient(create())
def test_get_message(client):
result = client.simulate_get('/')
assert result.status_code == 200

Related

Mocking a load time variable in Python

I am trying to get coverage up on my Python code and am testing my Flask app. Without being too specific, let's look at the sample code here:
# from /my_project/app_demo/app.py
from private_module import logs
from flask import Flask
import logging
logs.init_logging()
logger = logging.getLogger(__name__)
app = Flask(__name__)
#app.route("/greet/<name:name>", methods=["GET"])
def greet(name):
logger.info(f"{name} wants a greeting")
return f"Hello, {name}!"
if __name__ == "__main__":
app.run(debug=True)
If I am to write a unit test for the greet function, I want to mock the logger to assert it is called when it is created and when the info method is called. For this example, the sources root folder is app_demo and there is an init.py in that python folder.
# from /my_project/tests/test_app.py
import pytest
from unittest import mock
#pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_greet(client):
logs_mock = mock.patch("app_demo.app.logs.init_logging")
logger_mock = mock.patch("app_demo.app.logging.getLogger")
actual = client.get("George")
assert "Hello, George!" == actual
# these next assertions do not work
logs_mock.assert_called_once_with()
logging_mock.assert_called_once_with("app_demo.app") # not sure if this will be "__main__"
logging_mock.return_value.info.assert_called_once_with("George wants a greeting")
If I debug where the logger.info is called in the greet function, the object is a logger and not the mock object I want it to be.
I have tried making the mock in another fixture as well but that did not make it work either.

AttributeError on Mocked Class in Python

I am using mockito to unit test a program in Python. I have a class like:
import boto3
import datetime
class Cache:
def __init__(self):
client = boto3.resource('s3')
self.bucket_name = 'name'
self.bucket = client.Bucket(self.bucket_name)
def setup_cache(self, cache_file='cache.csv', cache_filepath='cache'):
cache_object = self.bucket.Object(cache_file)
if cache_object.last_modified < datetime.datetime.now():
self.bucket.download_file(cache_filepath, cache_file)
else:
print('Cache already up to date')
def main():
cache = Cache()
cache.setup_cache()
And the test code I am getting stuck on is this:
from mockito import mock, when
import datetime
import boto3
import mock_cache
class TestMockCache:
def test_last_mod(self):
mock_client = mock()
when(boto3).resource('s3').thenReturn(mock_client)
mock_bucket = mock()
when(mock_client).Bucket('name').thenReturn(mock_bucket)
mock_bucket.last_modified = datetime.datetime.now()
mock_cache.main()
When running pytest on the unit test, I am getting thrown this attribute error:
AttributeError: 'NoneType' object has no attribute 'last_modified'
From the documentation it looked like I could assign 'cache_mock.last_modified' like this. However, I also tried:
when(cache_mock).last_modified.thenReturn(test_date)
and got:
AttributeError: 'StubbedInvocation' object has no attribute 'thenReturn'
Which I don't fully understand, but assume that means a mockito mock() object can't have multiple return values?
Any help with this would be appreciated. I feel like I am misunderstanding something fundamental about either how mockito's mock works or mocking in general.
The patch from the Mock can be used to mock the client from boto3. Do not have to return anything from the client, as it is being mocked on the whole.
e.g.:
from folder.file import your_func
from unittest.mock import patch
class TestSomething(unittest.TestCase):
#patch("botocore.client.BaseClient._make_api_call")
def test_something(self, _mock_client_boto3):
your_func()
.. do something
In case you want to return something from the client, it could be achieved by specifying the return_value in the patch as shown below:
from folder.file import your_func
from unittest.mock import patch
class TestSomething(unittest.TestCase):
#patch("botocore.client.BaseClient._make_api_call", return_value=None)
def test_something(self, _mock_client_boto3):
your_func()
.. do something

Python Mock Test

I am trying to mock test an endpoint that gets the time and date.
I have viewed several tutorials and python docs, but I am still getting stumped by the mock test.
Any help is appreciated greatly
from flask import Flask, redirect, url_for
import json
import urllib.request
import requests
app = Flask(__name__)
#app.route('/')
def welcome():
return "Hello"
#app.route('/<zone>')
def Endpoint(zone):
address = f"http://worldclockapi.com/api/json/{zone}/now"
response = urllib.request.urlopen(address)
result = json.loads(response.read())
time = result['currentDateTime']
return time
if __name__ == "__main__":
app.run(debug=True)
My attempt.
I think I am still calling the external element.
I want to use a fake JSON string and actually mock with that.
The first test passes when I run it. But I don't think it is a true mock.
#!/usr/bin/python
import unittest
from unittest import TestCase
from unittest.mock import patch, Mock
#name of endpoint program
import question
class TestingMock(TestCase):
#patch('question.Endpoint')
def test_call(self, MockTime):
current = MockTime()
current.posts.return_value = [
{"$id": "1", "currentDateTime": "2020-07-17T12:31-04:00", "utcOffset": "-04:00:00"}
]
response = current.posts()
self.assertIsNotNone(response)
self.assertIsInstance(response[0], dict)
#patch('question.Endpoint')
def test_response(mock_get):
mock_get.return_value.ok = True
respond = question.Endpoint()
assert_is_not_none(respond)
if __name__ == '__main__':
unittest.main()
You are conflicting with your root URL handler. Try changing #app.route('/<zone>') to #app.route('/time/<zone>'), then navigate to that url

write unit test for web.py application by using pytest

I would like to write unit test for web.py application by using pytest. How to invoke the web.py services in pytest.
Code:
import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def GET(self):
return "Hello, world!"
if __name__ == "__main__":
app.run()
It can be done by using python requests module, when we run the web.py services, it will run http://localhost:8080/. Then import requests module and use get method and in response object, you can verify the result. That's fine.
By using Paste and nose also we can achieve this as per web.py official documentation. http://webpy.org/docs/0.3/tutorial.
Is there any solution in pytest like option in paste and nose.
Yes. Actually the code from the web.py recipe Testing with Paste and Nose could be used with py.test almost as is, just removing the nose.tools import and updating assertions appropriately.
But if you want to know how to write tests for web.py applications in the py.test style, they could look like this:
from paste.fixture import TestApp
# I assume the code from the question is saved in a file named app.py,
# in the same directory as the tests. From this file I'm importing the variable 'app'
from app import app
def test_index():
middleware = []
test_app = TestApp(app.wsgifunc(*middleware))
r = test_app.get('/')
assert r.status == 200
assert 'Hello, world!' in r
As you will add further tests, you will probably refactor creation of the test app out to a fixture:
from pytest import fixture # added
from paste.fixture import TestApp
from app import app
def test_index(test_app):
r = test_app.get('/')
assert r.status == 200
assert 'Hello, world!' in r
#fixture()
def test_app():
middleware = []
return TestApp(app.wsgifunc(*middleware))

Mocking module global variable while import

I am writing unit tests for the api, which connects to MongoDB. In my API module it looks like this:
from flask import Flask, jsonify
from MyApp import MongoData
api = Flask(__name__)
DB_CONN = MongoData()
#api.route('/bla', methods=['GET'])
def alive():
return jsonify({'response': true})
I have a problem while importing this module in my unittest. I want to mock collection from MongoData() with special mock class, which uses mongomock. The problem is that I cannot mock DB_CONN while importing in tests:
from MyApp import api
I was trying to do this with mock:
DB_CONN = MockMongoData()
with mock.patch('MyApp.api.DB_CONN', DB_CONN):
from MyApp import api
but it still tries to connect to the database as specified in the config file.
Any advice how to mock DB_CONN from MyApp.api module?
Thanks in advance!
EDIT:
This will work:
import sys
from MyApp import MongoData, MockMongoData
sys.modules['MyApp'].MongoData = MockMongoData
from MyApp import api
But is there a better (more pythonic) way to do this?
Import the module first, then monkeypatch its members:
DB_CONN = MockMongoData()
from MyApp import api
with mock.patch('MyApp.api.DB_CONN', DB_CONN):
api.run()

Categories

Resources