Unittest for flask app not running as expected - python-3.x

I have created a simple unit test for my flask app, these tests are saved in a different folder, and then called from the manage.py file. However, when I run the tests, none of the test functions is executed.
Here is the test.py file:
from flask import current_app
import unittest
from app import create_app, db
class BasicTestCase(unittest.TestCase):
def setUp(self):
self.app=create_app('testing')
self.app_context=self.app.app_context()
self.app_context.push()
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_app_exists(self):
self.assertFalse(current_app is None)
def test_app_is_testing(self):
self.assertTrue(current_app.config['TESTING'])
Also here is the manage.py file.
from app import create_app
app=create_app('default')
def test():
import unittest
tests=unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
if __name__=='__main__':
app.run(debug=True)
When I run the test() function from the command line, I get the following response.
`----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK`
Is there something is am doing wrong?

Related

Starting a Flask Server from Within Test Code?

I'm working through the Flasky tutorial from Miguel Grinberg's book Flask Web Development 2e and I've run into a snag with the end-to-end testing in Chapter 15. When I try to run the code I get a console message
* Ignoring a call to 'app.run()' that would block the current 'flask' CLI command.
Only call 'app.run()' in an 'if __name__ == "__main__"' guard.
followed by the browser reporting "Firefox cannot establish a connection..." This suggest to me that the test server is not starting.
Here's the code, from pages 231-233 of the book (the file is tests/test_selenium.py):
import threading
import unittest
from selenium import webdriver
from app import create_app, db, fake
from app.models import Role, User
class SeleniumTestCase(unittest.TestCase):
browser = None
#classmethod
def setUpClass(cls) -> None:
try:
cls.browser = webdriver.Firefox()
except Exception as e:
pass
if cls.browser:
cls.app = create_app('testing')
cls.app_context = cls.app.app_context()
cls.app_context.push()
import logging
logger = logging.getLogger('werkzeug')
logger.setLevel('ERROR')
db.create_all()
Role.insert_roles()
fake.users(10)
fake.posts(10)
admin_role = Role.query.filter_by(permissions=0xff).first()
admin = User(email='john#example.com', password='cat', username='john', role=admin_role, confirmed=True)
db.session.add(admin)
db.session.commit()
cls.server_thread = threading.Thread(
target=cls.app.run,
kwargs={
'debug': 'false',
'use_reloader': False,
'use_debugger': False,
'host': '0.0.0.0',
'port': 5000
}
)
cls.server_thread.start()
#classmethod
def tearDownClass(cls) -> None:
if cls.browser:
cls.browser.get('http://localhost:5000/shutdown')
cls.browser.quit()
cls.server_thread.join()
db.drop_all()
db.session.remove()
cls.app_context.pop()
def setUp(self) -> None:
if not self.browser:
self.skipTest('Web browser not available')
def tearDown(self) -> None:
pass
def test_admin_home_page(self):
self.browser.get('http://localhost:5000/') # fails here
self.assertRegex(self.browser.page_source, 'Hello,\s+Stranger!')
self.fail('Finish the test!')
How can I get a test server up and running from within the test code? (I putzed around with Flask-Testing for a few days before giving it up as unmaintained.)
ADDENDUM: Further experimentation has determined that the problem lies in the explicit call to app.run() conflicting with the Flask CLI's implicit call to app.run(), but without the explicit call the test server doesn't start.
I want to run this from the Flask CLI the same as my unit tests. This means I need to find a way to start the test server after the test database is populated, which happens after the test class's code begins to run. The CLI command code is:
#app.cli.command()
#click.argument('test_names', nargs=-1)
def test(coverage, test_names):
"""Run the unit tests"""
import unittest
if test_names:
tests = unittest.TestLoader().loadTestsFromNames(test_names)
else:
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
so running from __main__ would bypass the tests' load/run sequence.
I found a feasible solution using Timer
import unittest
from threading import Timer
Create two variables on top in your code
timer = None
myapp = None
class ApplicationTest(unittest.TestCase):
Now at the bottom of the file create main method and custom method of timer, I assume startTest as method name
In the main method you can call create_app and put it in global variable and use that myapp variable inside your selenium testing code
unittest.main() will manually trigger your test class and run the test cases one after one, unfortunately the test runs twice, I don't know why
def startTest():
timer.cancel()
unittest.main()
if __name__ == '__main__':
timer = Timer(6.0, startTest)
timer.start()
myapp = create_app()
myapp.run(debug=True, threaded=True)

flask ignoring subprocess.call

I have been trying to create a webpage which allows people to start gameservers with just a simple GUI. Im using flask, and have made a very simple page which should pass CLI commands to the os. Right now flask runs the whole function except for the subprocess call which it just seems to ignore. The commands work when pasting the commands manually.
the script:
from flask import render_template
command = 'cd /home/gameserver/PaperMC/ && ./pmcserver start'
def minecraftscript():
call(command, shell=True)
return render_template('index.html')
main.py:
from scripts import *
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/controlpanel')
def login():
return render_template('controlpanel.html')
#app.route('/')
def startminecraft():
minecraftpy.minecraftscript()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=81, debug=True)

Pytest: no tests ran

I have the following class file and a corresponding test file
dir.py:
import os
class Dir:
def __init__(self, path=''):
self.path = path
#property
def path(self):
return self._path
#path.setter
def path(self, path):
abspath = os.path.abspath(path)
if abspath.exists():
self._path = path
else:
raise IOError(f'{path} does not exist')
and dir_test.py:
import unittest
from ..dir import Dir
class TestDir(unittest.TestCase):
def IOErrorIfPathNotExists(self):
with self.assertRaises(IOError):
Dir.path = "~/invalidpath/"
with self.assertRaises(IOError):
Dir('~/invalidpath/')
if __name__ == "__main__":
unittest.main()
but when I run
pytest -x dir_test.py
it just prints no tests ran in 0.01 seconds
and I have no idea why. It is my first time using pytest except with exercises from exercism.io, and I can't spot any difference to their test files.
I am running it in a virtual environment (Python 3.6.5), with pytest and pytest-cache installed via pip.
That's because your test method is not named properly.
By default, pytest will consider any class prefixed with Test as a test collection.
Yours is TestDir, this matches.
By default, pytest will consider any function prefixed with test as a test.
Yours is IOErrorIfPathNotExists, which does not start with test and is not executed.
Source.

Scrapy - Pytest execution not executing pipeline

I have this Scrapy project with its default structure and I added a test package with a test module.
So this is my structure:
scraping/
scrapy.cfg
crawler/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
submissions_spider.py
test/
__init__.py
test_request.py
First, before start testing with py.test framework, I just wrote a main function inside this module running a new CrawlerProcess that invokes my spider.
Something like this:
from scraping.crawler.spiders.submissions_spider import SubmissionsSpider
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
if __name__ == '__main__':
crawler = CrawlerProcess(get_project_settings())
crawler.crawl(SubmissionsSpider, n=2)
crawler.start()
This will execute perfectly on pycharm debugging. It runs my SubmissionsSpider and then process the items with my defined pipeline.
But... When I write a simple test just to execute the same routine above and run "py.test", the processing of the items with my pipeline is not executed. The process scrapes the data and nothing else.
This is my new test file:
import pytest
from scraping.crawler.spiders.submissions_spider import SubmissionsSpider
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from twisted.python.failure import Failure
#pytest.fixture
def crawler():
return CrawlerProcess(get_project_settings())
def test_crawler_execution(crawler):
crawler.crawl(SubmissionsSpider, n=2)
crawler.start()
I suspect it could be something in the configuration.
scrapycfg file:
[settings]
default = crawler.settings
[deploy]
#url = http://localhost:6800/
project = crawler
settings.py:
BOT_NAME = 'crawler'
SPIDER_MODULES = ['crawler.spiders']
NEWSPIDER_MODULE = 'crawler.spiders'
ROBOTSTXT_OBEY = True
ITEM_PIPELINES = {
'crawler.pipelines.MongoWriterPipeline': 1,
}
Any idea, folks?

Python: Use fixture for unittest.TextTestRunner.run()

Have defined Cases.py file with defined suite:
import unittest
import pytest
from adminzone_tests.Clients import TestClients
def collect_suite():
suite = unittest.TestSuite()
suite.addTest(TestClients)
return suite
#pytest.mark.usefixtures('admin_session')
def run():
unittest.TextTestRunner(verbosity=2).run(collect_suite())
#pytest.mark.usefixtures('client_session')
def run():
unittest.TextTestRunner(verbosity=2).run(collect_suite())
conftest.py file:
import pytest
from base.Common import Common
from base.Users import Users
#pytest.fixture(scope='session')
def admin_session(request):
Users.users('admin')
def admin_session_teardown():
Common.logout()
request.addfinalizer(admin_session_teardown)
#pytest.fixture(scope='session')
def client_session(request):
Users.users('client')
def client_session_teardown():
Common.logout()
request.addfinalizer(client_session_teardown)
Idea: execute the same suite one by one with different user's sessions.
But fixture executes only if has param autouse=True
How to make fixture executable without autouse=True and run the same suite with different setup ?
Finally i found solution that i was looking for, could be useful for somebody:
Used hook with command line args
conftest.py file:
import pytest
def pytest_addoption(parser):
# adding command line arg --user
parser.addoption('--users', action='append', default=['user1'])
def pytest_generate_tests(metafunc):
if 'users' in metafunc.fixturenames:
us = set(metafunc.config.option.users)
if not us:
us = ['user1']
elif 'all' in us:
us = ['user2', 'user1']
else:
us = list(us)
metafunc.parametrize('users', us, scope='session')
#pytest.yield_fixture(scope='session', autouse=True)
def define_users(request, users):
# Execute login here, as example called function with param of user_type
login(users)
def teardown():
# Execute logout here
logout()
request.addfinalizer(teardown)
Cases.py file:
import unittest
suite = unittest.TestSuite()
def collect_suite():
suite.addTest(TestClass1)
# adding more test classes here
def run():
unittest.TextTestRunner(verbosity=2).run(suite)
Now via command line execute command(in project's root dir)
python -m pytest Cases.py -s -v -n auto --users all
Test suite will be executed with login as user1, after will be executed with login as user2

Resources