ibrewster
ibrewster

Reputation: 3622

Python - CherryPy testing - set session data?

When running a pytest unit test against a CherryPy server, using a cherrypy.helper.CPWebCase sub-class, how can I set data for the session object? I tried just calling cherrypy.session['foo']='bar' like I would if I was really in a cherrypy call, but that just gave an "AttributeError: '_Serving' object has no attribute 'session'"

For reference, a test case might look something like this (pulled from the CherryPy Docs with minor edits):

import cherrypy
from cherrypy.test import helper
from MyApp import Root

class SimpleCPTest(helper.CPWebCase):
    def setup_server():
        cherrypy.tree.mount(Root(), "/", {'/': {'tools.sessions.on': True}})

    setup_server = staticmethod(setup_server)

    def check_two_plus_two_equals_four(self):
        #<code to set session variable to 2 here>
        # This is the question: How do I set a session variable?
        self.getPage("/")
        self.assertStatus('200 OK')
        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
        self.assertBody('4')

And the handler might look something like this (or anything else, it makes no difference whatsoever):

class Root:
    @cherrypy.expose
    def test_handler(self):
        #get a random session variable and do something with it
        number_var=cherrypy.session.get('Number')
        # Add two. This will fail if the session variable has not been set,
        # Or is not a number
        number_var = number_var+2
        return str(number_var)

It's safe to assume that the config is correct, and sessions work as expected.

I could, of course, write a CherryPy page that takes a key and value as arguments, and then sets the specified session value, and call that from my test code (EDIT: I've tested this, and it does work). That, however, seems kludgy, and I'd really want to limit it to testing only somehow if I went down that road.

Upvotes: 1

Views: 1469

Answers (1)

What you are trying to achieve is usually referred as mocking.

While running tests you'd usually want to 'mock' some of resources you access with dummy objects having same interface (duck typing). This may be achieved with monkey patching. To simplify this process you may use unittest.mock.patch as either context manager or method/function decorator.

Please find below the working example with context manager option:

==> MyApp.py <==

import cherrypy


class Root:
    _cp_config = {'tools.sessions.on': True}

    @cherrypy.expose
    def test_handler(self):
        # get a random session variable and do something with it
        number_var = cherrypy.session.get('Number')
        # Add two. This will fail if the session variable has not been set,
        # Or is not a number
        number_var = number_var + 2
        return str(number_var)

==> cp_test.py <==

from unittest.mock import patch

import cherrypy
from cherrypy.test import helper
from cherrypy.lib.sessions import RamSession

from MyApp import Root


class SimpleCPTest(helper.CPWebCase):
    @staticmethod
    def setup_server():
        cherrypy.tree.mount(Root(), '/', {})

    def test_check_two_plus_two_equals_four(self):
        # <code to set session variable to 2 here>
        sess_mock = RamSession()
        sess_mock['Number'] = 2
        with patch('cherrypy.session', sess_mock, create=True):
            # Inside of this block all manipulations with `cherrypy.session`
            # actually access `sess_mock` instance instead
            self.getPage("/test_handler")
        self.assertStatus('200 OK')
        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
        self.assertBody('4')

Now you may safely run test as follows:

 $ py.test -sv cp_test.py
============================================================================================================ test session starts =============================================================================================================
platform darwin -- Python 3.5.2, pytest-2.9.2, py-1.4.31, pluggy-0.3.1 -- ~/.pyenv/versions/3.5.2/envs/cptest-pyenv-virtualenv/bin/python3.5
cachedir: .cache
rootdir: ~/src/cptest, inifile:
collected 2 items

cp_test.py::SimpleCPTest::test_check_two_plus_two_equals_four PASSED
cp_test.py::SimpleCPTest::test_gc <- ../../.pyenv/versions/3.5.2/envs/cptest-pyenv-virtualenv/lib/python3.5/site-packages/cherrypy/test/helper.py PASSED

Upvotes: 4

Related Questions