Unit test a Flask session - cannot reproduce failure with session_transaction - flask

I am testing a Flask application (Flask 0.9), and in particular I have a session fixture that I would like to run in the documented-way, being something like this (as I understand it):
from flask import Flask, session
app = Flask(__name__)
#app.route('/', methods=['POST'])
def m():
logging.error(session) # expect {'x': 1}
return ""
with app.test_request_context() as trc:
with app.test_client() as c:
with c.session_transaction() as sess:
sess['x'] = 1
c.post()
This works as expected, with the output being something like this:
ERROR:root:<SecureCookieSession {'x': 1}>
Unfortunately I am encountering an unexpected result where the session data is not set in the endpoint function, i.e. the output is something like this:
ERROR:root:<SecureCookieSession {}>
This issue exhibits only when run from my unit testing framework. As it stands, I am unable to reproduce this problem with a degenerate case, though I have made a fairly substantial effort with a gist of some of this effort here. The salient points being that I have included itsdangerous and Google App Engine testbed, expecting maybe one of them to have been the cause.
On my own system I have gone further than the gist, and almost completely replicated my unit test framework trying to isolate this. Likewise, I have removed ever-increasing amounts of relevant code from my testing framework. To the point, I am unable to think of differences between the degenerate case and my stripped-down framework that could influence the outcome. I have traversed the c.post() call in pdb to try eek out the cause of this malignity, but have yet to glean any useful insight.
Which is all to say, I would be grateful for a little direction or suggestion as to where the issue may lie. What could possibly be influencing the Werkzeug context in such a way that the session_transaction is not being honoured?

In my case, I was restricting cookies to a specific domain automatically by loading a configuration file. By updating the configuration on-the-fly, I was able to get cookies to work while unit testing. Setting the SESSION_COOKIE_DOMAIN property to None, all domains (namely localhost) were able to set sessions.
app.config.update(
SESSION_COOKIE_DOMAIN = None
)
You may want to fiddle around with the configuration settings described under Configuration Handling in the docs.

I hate to resurrect an old question, but I believe that I figured out the solution to this issue. For testing, try setting your server name to localhost:
app.config['SERVER_NAME'] = 'localhost'
I was originally using Brian's hack, but this solved the problem for me.

Without a testcase that actually fails it's hard to say much anything. All I can think of is that the TestClient instance you're using the make the request is different from the one you use to set up the session. E.g. you could make the gist fail as expected with this:
with self.app.test_client() as c:
with c.session_transaction() as sess:
sess['d'] = 1
self.client.post()
But as this isn't the case here or the gist, go figure.

Here is what I ended up doing:
# Code that modifies the current `session`; we must be in a test context already.
headers = {}
with self.client.session_transaction():
# Get the cookie that is sent to the browser to establish a connection
# via a fake request.
response = make_response()
self.app.session_interface.save_session(self.app, session, response)
headers['Cookie'] = response.headers.get('Set-Cookie', '')
self.client.post(..., headers=headers) # also works for .get, .put, etc.

Related

Patch a class method used in a view

I'm trying to test my view given certain responses from AWS. To do this I want to patch a class I wrote to return certain things while running tests.
#patch.object(CognitoInterface, "get_user_tokens", return_value=mocked_get_user_tokens_return)
class TestLogin(TestCase):
def test_login(self, mocked_get_user_tokens):
print(CognitoInterface().get_user_tokens("blah", "blah")) # Works, it prints the patched return value
login_data = {"email": "whatever#example.com", "password": "password"}
response = self.client.post(reverse("my_app:login"), data=login_data)
Inside the view from "my_app:login", I call...
CognitoInterface().get_user_tokens(email, password)
But this time, it uses the real method. I want it to use the patched return here as well.
It seems my patch only applies inside the test file. How can I make my patch apply to all code during the test?
Edit: Never figured out why #patch.object wasn't working. I just used #patch("path.to.file.from.project.root.ClassName.method_to_patch").
Also, see: http://bhfsteve.blogspot.com/2012/06/patching-tip-using-mocks-in-python-unit.html
Most likely CognitoInterface is imported in the views.py earlier than the monkeypatching happens. You can check this by adding print() into the views where CognitoInterface is imported (or declared, it's not obvious from the amount of code here), and then another print() next to the monkeypatching.
If the monkeypatching happens latter, you have to either delay the import, or in worst case monkeypatch in both places.

How to unit test Google Home functions?

The way my Google Home app is organizaed is a class containing all intent methods and then
at the bottom a line
exports.myapp = functions.https.onRequest(
(request, response) => new MyApp(request, response).run()
);
Since the functions in MyApp depend on request to get parameters from, I assume a unit test has to have a faked input request correct?
From the Actions On Google simulator, I've been able to get requests, but I'm not sure how to fake the response for the response parameter to `MyApp constructor, especially since the returning thing from Actions on Google seems to be after running my actual code.
All of the following would be helpful: generic code snippets, links to test files/cases, request/response constructor arguments, etc.
Thank you!

ServiceStack View/Template control when exception occurs?

I added some razor views and use a request filter to check browser version and switch between desktop and mobile views. But when a exception occurs, especially validation exception, it seems the framework return immediately and never touched any custom code. I tried request/response filter, service exception handler, none got executed. It seems to ignore view/template specified in URL query string as well.
Is there way to set view/template during exception? Thanks
The first question is how are you handling validation exceptions?
the most common procedure to perform this kind of task is by using the fluentValidation, the response can return a message for more than one validation at the time, all the validations are against DTOs and you´ll need to implement an AbstractValidator, the first thing you need to do is to register the validators that belons to your applciation like the following:
Plugins.Add(new ValidationFeature());
container.RegisterValidators(typeof(CredentialsAuthValidator).Assembly);
I´m valdiating in this case that the Auth username and password should not be Empty, take a look to the following example:
public class CredentialsAuthValidator : AbstractValidator<ServiceStack.ServiceInterface.Auth.Auth>
{
public CredentialsAuthValidator()
{
RuleSet(ApplyTo.Post, () =>
{
RuleFor(x => x.UserName).NotNull().WithMessage("Username Required").When(x => x.provider == "Credentials");
RuleFor(x => x.Password).NotNull().WithMessage("Password Required").When(x => x.provider == "Credentials");
}
);
}
}
if some of the validation fails you´ll get a responseStatus from the server with the errorCode and the messages.
You can configure a custom httpHandlers in the case you would like to have a handler for specific scenarios or a global error handler, this can be performed in your serviceHost configuration, something like this:
GlobalHtmlErrorHttpHandler = new RazorHandler("/views/error"),
CustomHttpHandlers =
{
{HttpStatusCode.NotFound, new RazorHandler("/views/notfound")},
{HttpStatusCode.Unauthorized, new RazorHandler("/views/login")},
{HttpStatusCode.Forbidden, new RazorHandler("/views/forbidden")},
}
Thanks for the help from Pedro, and especially mythz from ServiceStack. Now I think I start to understand my problems.
ServiceStack is first and foremost a service framework and Razor is just another view over the same result. But I was a little hesitate with a full on client side solution and keep falling back to familiar territory and looking for some kind of code-behind feature. That seems to be the root of lots of my struggles.
After some more research, this is what I come up so far.
ServiceStack for service, of course.
Razor view to build the basic layout and the main page for each major feature
Build a json script tag from model to hold initial data, like in SS's HTML Report
Jquery and Eldarion ajax for all subsequent in-page processing
Handlebars for javascript templating
Verifyjs for validation
So far look promising. Pages are lot smaller in size, running super smooth and mostly pure json flying over the wire.
Still a work in progress, all suggestions welcome.
ViewSwitch works when I changed to use Request Filter instead. Got the correct layout and all the references, etc. Although they have to share the same error page, but there's not much formatting in there.

Unit Testing with django-pipeline

I have problems unit testing views of an application that uses django-pipeline? Whenever I perform a client.get() on any URL, it produces the following exception:
ValueError: The file 'css/bootstrap.css' could not be found with <pipeline.storage.PipelineCachedStorage object at 0x10d544950>.
The fact that it is bootstrap.css is of course not important, but that I'm unable to execute view rendering due to this exception.
Any guide / tips are welcome!
I ran into a similar problem. However, setting
STATICFILES_STORAGE='pipeline.storage.NonPackagingPipelineStorage'
when running the tests only partly solved my issue. I also had to disable the pipeline completely if you want to run LiverServerTestCase tests without having to calling 'collecstatic' before running the tests:
PIPELINE_ENABLED=False
Since django 1.4 it's fairly easy to modify settings for tests - there is a handy decorator that works for methods or TestCase classes:
https://docs.djangoproject.com/en/1.6/topics/testing/tools/#overriding-settings
e.g.
from django.test.utils import override_settings
#override_settings(STATICFILES_STORAGE='pipeline.storage.NonPackagingPipelineStorage', PIPELINE_ENABLED=False)
class BaseTestCase(LiveServerTestCase):
"""
A base test case for Selenium
"""
def setUp(self):
...
However this produced inconsistent results as #jrothenbuhler describes in his answer. Regardless, this is less than ideal if you are running integration tests - you should mimic production as much as possible to catch any potential issues. It appears django 1.7 has a solution for this in the form of a new test case "StaticLiveServerTestCase". From the docs:
https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#django.contrib.staticfiles.testing.StaticLiveServerCase
This unittest TestCase subclass extends
django.test.LiveServerTestCase.
Just like its parent, you can use it to write tests that involve
running the code under test and consuming it with testing tools
through HTTP (e.g. Selenium, PhantomJS, etc.), because of which it’s
needed that the static assets are also published.
I haven't tested this, but sounds promising. For now I'm doing what #jrothenbuhler in his solution using a custom test runner, which doesn't require you to run collectstatic. If you really, really wanted it to run collectstatic you could do something like this:
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
from django.core.management import call_command
class CustomTestRunner(DjangoTestSuiteRunner):
"""
Custom test runner to get around pipeline and static file issues
"""
def setup_test_environment(self):
super(CustomTestRunner, self).setup_test_environment()
settings.STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage'
call_command('collectstatic', interactive=False)
In settings.py
TEST_RUNNER = 'path.to.CustomTestRunner'
I've been running into the same problem. I tackled it using a custom test runner:
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
from pipeline.conf import settings as pipeline_settings
class PipelineOverrideRunner(DjangoTestSuiteRunner):
def setup_test_environment(self):
'''Override STATICFILES_STORAGE and pipeline DEBUG.'''
super(PipelineOverrideRunner, self).setup_test_environment()
settings.STATICFILES_STORAGE = 'pipeline.storage.PipelineFinderStorage'
pipeline_settings.DEBUG = True
Then in your settings.py:
TEST_RUNNER = 'path.to.PipelineOverrideRunner'
Setting pipeline's DEBUG setting to True ensures that the static files are not packaged. This prevents the need to run collectstatic before running the tests. Note that it's pipeline's DEBUG setting, not Django's, which is overridden here. The reason for this is that you want Django's DEBUG to be False when testing to best simulate the production environment.
Setting STATICFILES_STORAGE to PipelineFinderStorage makes it so that the static files are found when Django's DEBUG setting is set to False, as it is when running tests.
The reason I decided to override these settings in a custom test runner instead of in a custom TestCase is because certain things, such as the django.contrib.staticfiles.storage.staticfiles_storage object, get set up once based on these and other settings. When using a custom TestCase, I was running into problems where tests would pass and fail inconsistently depending on whether the override happened to be in effect when modules such as django.contrib.staticfiles.storage were loaded.
I ran into the same problem. I managed to get around it by using a different STATIC_FILES_STORAGE when I'm testing:
STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage'
I have separate settings files for production and testing, so I just put it in my test version, but if you don't, you could probably wrap it in if DEBUG.
--EDIT
It took a little more effort, because this can only present during the unittesting. To address that, I used the snippet at http://djangosnippets.org/snippets/1011/ and created a UITestCase class:
class UITestCase(SettingsTestCase):
'''
UITestCase handles setting the Pipeline settings correctly.
'''
def __init__(self, *args, **kwargs):
super(UITestCase, self).__init__(*args, **kwargs)
def setUp(self):
self.settings_manager.set(
STATICFILES_STORAGE='pipeline.storage.NonPackagingPipelineStorage')
Now all of my tests that need to render UI that incude compressed_css tags use UITestCase instead of django.test.TestCase.
I ran into the same problem, and it turned out to be because I had
TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'
I don't understand how, but it must somehow have interacted with Pipeline. Once I removed that setting, the problem went away.
I still needed to force Celery to be eager during testing, so I used override_settings for the tests that needed it:
from django.test.utils import override_settings
…
class RegisterTestCase(TestCase):
#override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
CELERY_ALWAYS_EAGER=True,
BROKER_BACKEND='memory')
def test_new(self):
…
Same here. Refers to this issues: https://github.com/cyberdelia/django-pipeline/issues/277
As I use py.test, I put this in conftest.py as a workaround:
import pytest
from django.conf import settings
def pytest_configure():
# workaround to avoid django pipeline issue
# refers to
settings.STATICFILES_STORAGE = 'pipeline.storage.PipelineStorage'
i've tried #jrothenbuhler workaround and it helps at first..
but then, for some reason it starts fail again with same error
after hours of debugging i've figured out that the only things that helps is to set
STATICFILES_STORAGE = 'pipeline.storage.NonPackagingPipelineStorage'
directly in settings...
dunno why, but it works.

how properly delete cookies?

I use Selenium Webdriver, I want clear cookies before execute test. I use code from official 'Selenium` site. This is code:
Cookie cookie = new Cookie("key", "value");
driver.manage().addCookie(cookie);
Set<Cookie> allCookies = driver.manage().getCookies();
for (Cookie loadedCookie : allCookies) {
System.out.println(String.format("%s -> %s", loadedCookie.getName(), loadedCookie.getValue()));
}
driver.manage().deleteAllCookies();
But I get notification: - Cookie cannot be resolved to a type, Set cannot be resolved to a type
The Set is of java.util package.
The Cookie is of org.openqa.selenium package.
You need to import those two classes to make your code work:
import java.util.Set;
import org.openqa.selenium.Cookie;
To make the suffering less painful, every modern Java IDE has an automatic function for this:
In Eclipse, it's called "Organize imports" and sits under Ctrl+Shift+O.
In IntelliJ, it's called "Optimize imports" and sits under Ctrl+Alt+O.
In NetBeans, it's also called somehow and sits under Ctrl+Shift+I.
I'd check your imports. I suspect that you are using the javax cookie when you need the selenium one.
javax.servlet.http.Cookie
org.openqa.selenium.Cookie