Reputation: 987
I am trying to write unit tests for a Tornado web app and I have trouble on how to properly test code that uses the options module. I define several options and access them in multiple places in the application, hence, I want to test different option combinations in the unit test.
A very simple example that contains everything would be:
import tornado.web
from tornado.options import define, options
import tornado.ioloop
def define_options():
define('myoption', type=str, default='MyValue')
class ExampleHandler(tornado.web.RequestHandler):
def get(self):
self.write({
'myoption': options.myoption
})
def make_app():
ROUTES = [
(r'/', ExampleHandler)
]
return tornado.web.Application(ROUTES)
def run():
define_options()
app = make_app()
app.listen(8080)
tornado.ioloop.IOLoop.current().start()
if __name__ == '__main__':
run()
The big issue seems to be that the options are persisted over all tests that are being run and I don't see where I should call define_options
and how to change single test parameters without potentially affecting all other tests. Take the following example of a test code:
import tornado.testing
from demo import define_options, make_app
class Test1(tornado.testing.AsyncHTTPTestCase):
def setUp(self):
define_options()
super().setUp()
def get_app(self):
return make_app()
def test1(self):
response = self.fetch('/')
print(response)
This works fine, but as soon as I add a second test method:
def test2(self):
response = self.fetch('/')
print(response)
I will get an error like this:
File "/Users/jan/Documents/demo/demo/demo.py", line 7, in define_options
define('myoption', type=str, default='MyValue')
File "/Users/jan/anaconda/envs/py3k/lib/python3.4/site-packages/tornado/options.py", line 558, in define
callback=callback)
File "/Users/jan/anaconda/envs/py3k/lib/python3.4/site-packages/tornado/options.py", line 228, in define
(name, self._options[name].file_name))
tornado.options.Error: Option 'myoption' already defined in /Users/jan/Documents/demo/demo/demo.py
I can move the define_options
to setUpClass
but then I will see the same problem once I add a second test class. Hence, I am wondering if anyone has run into that issue and what solution I could use to run these tests. Not only where to put define_options
so it is only run once, but also how I could define a different set of options (starting from the defaults given in define_options
) for different tests without tests affecting each other.
Upvotes: 0
Views: 1055
Reputation: 22134
tornado.options
can be used in two ways:
tornado.options.define
at import time, access the results via tornado.options.options
, and parse the command line (or config file) with top-level functions in the tornado.options
module. In this example, you'd remove your define_options()
function and just define all your options at top level.tornado.options.OptionParser
objects. In this mode, you'd create an OptionParser()
object in your define_options()
function, call its define()
and parse methods instead of the functions in the options
module, and return it so that other code accesses the values through this object instead of
tornado.options.options`.Note that the global singleton style is the way that the tornado.options
module was originally designed and is intended to be used (it draws inspiration from Google's C++ gflags
package). One thing that can be a bit tricky in this style is to temporarily change a flag's value for a test. The unittest.mock
package has a little trouble with the magic objects used in tornado.options
, so you must use a helper method, mockable()
.
Upvotes: 1