I have developed an application using Falcon framework in Python language. I developed few API, Now I wanted to perform unit test so I used unittest package, But I am not able to test my APIs as it fails unittest.
For testing I refer this doc
I tried to simulate the get method but it asking me argument. Also I am unable to perform any put or post testing.
Here are my code to perform unit test
main.py
from app.api.v1 import scheduler
self.add_route('/v1/schedulers', scheduler.SchedulerCollection())
scheduler.py
class SchedulerCollection(BaseResource):
def __init__(self):
db = redis_db.RedisStorageEngine()
self.r = db.connection()
"""
Handle for endpoint: /v1/schedulers
"""
# #falcon.before(validate_scheduler_create)
def on_post(self, req, res):
#some code here
##falcon.before(auth_required)
def on_get(self, req, res):
#some code here
scheduler_test.py
import sys, os
import unittest
import json
from falcon import testing
from app.api.v1 import scheduler
class SchedulerTestCase(testing.TestCase):
def setUp(self):
super(SchedulerTestCase, self).setUp()
self.app = scheduler.SchedulerCollection.on_get
class TestMyApp(SchedulerTestCase):
def test_get_schedulers(self):
doc = {u'message': u'Hello world!'}
result = self.simulate_get('/v1/schedulers')
result = self.simulate_get('/v1/schedulers')
self.assertEqual(result.json, doc)
if __name__ == '__main__':
unittest.main()
After running test I am getting this error
Traceback (most recent call last):
File "tests/scheduler_test.py", line 17, in setUp
self.app = scheduler.SchedulerCollection().on_get(self.api_class)
TypeError: on_get() takes exactly 3 arguments (2 given)
I don't know where I am doing wrong
Any help would be highly appreciated
Thanks
First off, self.app should be initialize with your falcon app, not the method you are testing. Secondly, simulate_get is an attribute of self.app (not self).
Check more info at this answer.
Related
I want to use scrapy spider in Django views and I tried using CrawlRunner and CrawlProcess but there are problems, views are synced and further crawler does not return a response directly
I tried a few ways:
# Core imports.
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
# Third-party imports.
from rest_framework.views import APIView
from rest_framework.response import Response
# Local imports.
from scrapy_project.spiders.google import GoogleSpider
class ForFunAPIView(APIView):
def get(self, *args, **kwargs):
process = CrawlerProcess(get_project_settings())
process.crawl(GoogleSpider)
process.start()
return Response('ok')
is there any solution to handle that and run spider directly in other scripts or projects without using DjangoItem pipeline?
you didn't really specify what the problems are, however, I guess the problem is that you need to return the Response immediately, and leave the heavy call aka function to run in the background, you can alter your code as following, to use the Threading module
from threading import Thread
class ForFunAPIView(APIView):
def get(self, *args, **kwargs):
process = CrawlerProcess(get_project_settings())
process.crawl(GoogleSpider)
thread = Thread(target=process.start)
thread.start()
return Response('ok')
after a while of searching for this topic, I found a good explanation here: Building a RESTful Flask API for Scrapy
This simple web service works if I run it by hand but in my unit tests I get a 404 not found page as my response, preventing me to properly test the application.
normal behavior:
Folder structure:
/SRC
--web_application.py
/UNIT_TESTS
--test_wab_application.py
web_application.py
from flask import Flask, request, jsonify, send_from_directory
from python.Greeting import Greeting
application = Flask(__name__)
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='mega_developer',
DATABASE=os.path.join(app.instance_path, 'web_application.sqlite'),
)
try:
os.makedirs(app.instance_path)
except OSError:
pass
return app
#application.route('/greetings', methods=['GET', 'POST'])
def hello():
# GET: for url manipulation #
if request.method == 'GET':
return jsonify(hello = request.args.get('name', 'world', str))
test_web_application.py
import tempfile
import pytest
import web_application
class TestWebApplication:
app = web_application.create_app() # container object for test applications #
#pytest.fixture
def initialize_app(self):
app = web_application.create_app()
app.config['TESTING'] = True
app.config['DEBUG'] = False
app.config['WTF_CSRF_ENABLED'] = False
app.config['DATABASE'] = tempfile.mkstemp()
app.testing = True
self.app = app
def test_hello_get(self, initialize_app):
with self.app.test_client() as client:
response = client.get('/greetings?name=Rick Sanchez')
assert response.status_code == 200
test results (most relevant part only):
Launching pytest with arguments test_web_application.py::TestWebApplication::test_hello_get in C:\Users\Xrenyn\Documents\Projekte\Studium_Anhalt\QA&Chatbots Exercises\Exercise 2 - Web Service Basics\UNIT_TESTS
============================= test session starts =============================
platform win32 -- Python 3.8.0, pytest-5.2.2, py-1.8.0, pluggy-0.13.0 -- C:\Users\Xrenyn\Documents\Projekte\Studium_Anhalt\QA&Chatbots Exercises\Exercise 2 - Web Service Basics\VENV\Scripts\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\Xrenyn\Documents\Projekte\Studium_Anhalt\QA&Chatbots Exercises\Exercise 2 - Web Service Basics\UNIT_TESTS
collecting ... collected 1 item
test_web_application.py::TestWebApplication::test_hello_get FAILED [100%]
test_web_application.py:21 (TestWebApplication.test_hello_get)
404 != 200
Expected :200
Actual :404
So far I have tested various alternative routing paths for the client.get() method in test-web_application.py , including combinations like '/SRC/greetings?name=Rick Sanchez' or '../SRC/greetings?name=Rick Sanchez', but all to no different effect.
Do you have any idea on what I might be doing wrong or how I could get access to my web services' functions from within unit tests?
I think the problem is that you are creating two Flask instances. One with the name application that you add hello route to, and the second one using the create_app function. You need to create a test client using the application instance (the one you added the hello route to).
Can you import application and then obtain the client using application.test_client()?
Sample solution:
import pytest
from web_application import application
#pytest.fixture
def client():
with application.test_client() as client:
yield client
class TestSomething:
def test_this(self, client):
res = client.get('/greetings?name=Rick Sanchez')
assert res.status_code == 200
Checkout the official docs on testing.
I am attempting to create a Flask middleware in order to make py2neo transactions atomic. First I got a working outside of application context error, and I tried to apply this solution, as seen in the following code:
from flask import g
from py2neo import Graph
def get_db():
return Graph(password="secret")
class TransactionMiddleware(object):
def __init__(self, app):
self.app = app
with app.app_context(): # Error raises here.
g.graph = get_db()
g.transaction = g.graph.begin()
def __call__(self, environ, start_response):
try:
app_status = self.app(environ, start_response)
# some code
return app_status
except BaseException:
g.transaction.rollback()
raise
else:
g.transaction.commit()
But I got this error: AttributeError: 'function' object has no attribute 'app_context'.
I don't know if the solution I'm trying is not suitable for my case, or what is the problem.
You are in a WSGI middleware, so what gets passed in as an app is actually a method called Flask.wsgi_app the results of which you a later supposed to return from your __call__ handler.
Perhaps you can simply import your actual flask app and use app_context on that, as in:
from app import app # or whatever module it's in
# and then
class TransactionMiddleware(object):
...
def __call__(self, environ, start_response):
with app.app_context():
...
I would also consider just instantiating your Graph somehere on a module level. Perhaps next to your flask app instantiation and not attaching it to g. This way you can use it without application context:
# app.py
flask = Flask(__main__)
db = Graph(password="secret")
You can use it by directly importing:
# middleware.py
from app import db
I am writing some tests which use the python unittest package (Python 2.7)
and I heavily rely on the xmlrunner.XMLTestRunner to dump the XML test output
Unfortunately, I fail to find some basic example which describes how one can pass some command line options to the test class to parametrise some of the tests.
Does someone have some hint on how I could achieve this (using xmlrunner)?
In addition, here is what I try to achieve:
I define my tests in a set of classes in the following myunittest.py file:
import unittest
class TestOne(unittest.TestCase):
def __init__(self, options=None):
unittest.TestCase.__init__(self)
self.__options = options
def A(self):
print self.__options.configXML # try to print the parameter
self.assertEqual(1, 1)
and call it from the main.py which looks like:
from optparse import OptionParser
import unittest
import xmlrunner
from uitest import *
def runit(opt):
suite = unittest.TestSuite()
suite.addTest(TestOne(options=opt))
testrunner = xmlrunner.XMLTestRunner(output='tests', descriptions=True)
unittest.main(testRunner=testrunner).run(suite)
if __name__ == "__main__":
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("-c", "--configXML", dest="configXML", help="xml file")
options = parser.parse_args()
runit(opt=options)
Many thanks for your valuable help.
after several hours trying to figure it out, I came to this solution, which makes my day. I post it here, in case someone comes to the same issue.
The main drawback is that it seems I need to have everything in the same python file. and I run it like:
python test.py --xmlConfig=configFile.xml --xmlRunner
with test.py:
import unittest
import sys
from optparse import OptionParser
import xmlrunner
class MyTests(unittest.TestCase):
def testFirstThing(self):
xmlConfig=options.xmlConfig
self.assertEqual(xmlConfig,"configFile.xml")
if __name__ == '__main__':
parser = OptionParser()
parser.add_option("--xmlRunner", "--xmlRunner", help="perform a unittest and generate XML", dest="xmlRunner", default=False, action='store_true')
parser.add_option("--xmlConfig", "--xmlConfig", type="string", help="configuration file", dest="xmlConfig", default="config.xml")
options, arguments = parser.parse_args()
if options.xmlRunner:
del sys.argv[1:]
unittest.main(testRunner=xmlrunner.XMLTestRunner(output='./xml'))
I have the following three files.
app.py
from flask_restful import Api
from lib import globals
from flask import Flask
from flask.ext.cache import Cache
globals.algos_app = Flask(__name__)
#cache in file system
globals.cache = Cache(globals.algos_app, config={'CACHE_TYPE': 'filesystem', 'CACHE_DIR': '/tmp'})
api = Api(globals.algos_app)
api.add_resource(Test, '/test')
if __name__ == '__main__':
globals.algos_app.run(host='0.0.0.0', debug=True)
globals.py
global algos_app
global cache
Test.py
from flask_restful import Resource
from lib import globals
from flask_restful import Resource
import time
class Test(Resource):
def get(self):
return self.someMethod()
def post(self):
globals.cache.clear()
return self.someMethod()
#globals.cache.cached()
def someMethod(self):
return str(time.ctime())
I have a GET method which needs to the value from the cache and a POST method which updates the cache by first clearing the cache.
However, no matter I call the GET or the POST method, it always gives me the value from the cache.
PS: At the moment I am simply testing on the development server however I do need to deploy it using WSGI later.
I am not sure if it is the best way, but I did it using the following way.
class Test(Resource):
def get(self):
return globals.cache.get('curr_time')
def post(self):
result = self.someMethod()
globals.cache.set('curr_time', result, timeout=3600)
def someMethod(self):
return str(time.ctime())