In some Django Tests I have loop to test many things.
in the end-result it shows up as:
Ran 1 test in 3.456s
I would like to increment that counter for each loop, how can I do that?
It is using subTest() , but that does not update the counter (which I believe is a parameter testsRun)
my test looks something like this
class MyTestCase(TestCase):
def test_auth_pages(self):
pages = ['homepage', 'dashboard', 'profile']
for page in pages:
with self.subTest():
# ....testsRun += 1
self.c.login(username='test', password='test')
response = self.c.get(reverse_lazy(page))
self.assertEqual(200, response.status_code, msg=page)
self.c.logout()
response = self.c.get(reverse_lazy(page))
self.assertEqual(302, response.status_code, msg=page)
If you don't mind changing testing framework, consider pytest with pytest-django package. You can easily parametrize a test using #pytest.mark.parametrize:
import pytest
#pytest.mark.parametrize("page_name", ['homepage', 'dashboard', 'profile'])
def test_some_page(page_name, client):
client.login(username='test', password='test')
response = client.get(reverse_lazy(page))
assert response.status_code == 200
client.logout()
response = client.get(reverse_lazy(page))
assert response.status_code == 302
If not, you could create a test function factory that would accept the page name and return a test function for that page:
class MyTestCase(TestCase):
def _create_page_test(page_name):
def test_function(self):
self.c.login(username='test', password='test')
response = self.c.get(reverse_lazy(page_name))
self.assertEqual(200, response.status_code, msg=page_name)
self.c.logout()
response = self.c.get(reverse_lazy(page_name))
self.assertEqual(302, response.status_code, msg=page_name)
return test_function
test_homepage = _create_page_test("homepage")
test_dashboard = _create_page_test("dashboard")
test_profile = _create_page_test("profile")
The added benefit of such changes is that each page has a separate test, independent from the other. That makes debugging easier.
You can achieve this with a different test suite.
Check out test generators from the django-nose package
def test_evens():
for i in range(0, 5):
yield check_even, i, i*3 # this generates 5 different tests
def check_even(n, nn):
assert n % 2 == 0 or nn % 2 == 0
Related
I am getting weird results when testing for 2 different views. They are both list views. The first one should only show active orders(active=True), the second one only showing historic views (completed=True).
I am using postgresql as a database also. Issue is that both the view works perfectly fine in the browser, however when testing pytest raises an error saying that an order that shouldn't be listed, is listed.
The views are as follows:
class OrderHistoryView(LoginRequiredMixin, ListView):
template_name = 'orders/order_history.html'
def get_queryset(self):
user = self.request.user
qs = Order.objects.filter(Q(buyer=user, completed=True)|Q(seller=user, completed=True))
return qs
class OrderListView(LoginRequiredMixin, ListView):
template_name = 'orders/order_list_view.html'
def get_queryset(self):
user = self.request.user
qs = Order.objects.filter(Q(buyer=user, active=True)|Q(seller=user, active=True))
return qs
The tests are:
class OrderHistoryViewTests(TestCase):
#classmethod
def setUp(self):
self.req = RequestFactory()
self.user = mixer.blend('user.CustomUser', email='test#test.com', password='1234')
self.user2 = mixer.blend('user.CustomUser')
self.advert = mixer.blend('advert.Advert', author=self.user)
self.offer = mixer.blend(
'offer.Offer', advert=self.advert, author=self.user2, receiver=self.user, accepted=True)
self.order = mixer.blend(
'orders.Order', advert=self.advert, seller=self.user2, buyer=self.user, offer=self.offer,
pending=True, completed=True, active=True, disputed=False, cancelled=False)
self.order2 = mixer.blend('orders.Order', completed=False, buyer=self.user)
def test_only_shows_completed(self):
request = self.req.get('/')
request.user = self.user
resp = OrderHistoryView.as_view()(request)
self.assertContains(resp, self.order)
self.assertNotContains(resp, order2)
The second test is exactly the same, it just tests that its only showing active orders
error message is :
FAILED orders/tests/test_views.py::OrderHistoryViewTests::test_only_shows_completed - AssertionError: 3 != 0 : Response should not contain '10'
FAILED orders/tests/test_views.py::OrderListViewTests::test_order_listing_showing_only_active - AssertionError: 2 != 0 : Response should not contain '15'
EDIT:
I have done some further investigation on this and have found that even that the first "self.assertContains(resp, self.order)" statement is failing also. However when i run test_views only this "self.assertContains(resp, self.order)" test passes, however when i run all the tests together it fails. I have noticed after commenting out 5 different views in the test_forms directory, when i run the tests they all pass. Issue is that there is nothing wrong with the 5 form tests, their is no mock statements on them and the tear down seems to be working.
I then put a print statement to see what queryset is actually showing up:
printing Order.objects.all() shows ", ]>" and when i print(self.order.completed) also comes up true. However the result of the test failing says : "AssertionError: False is not true : Couldn't find '25' in response"
Even though 25 is clearly there and completed. The view code looks perfect also and the view is a very simple view. What could be causing this?
Also i have noticed if i run py.test i only need to comment out 3 of the form tests and it will all pass, however with manage.py test i have to comment out 5 form tests for it to pass.
I just used print(resp.context_data) to see what is actually in the response...
{'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': ]>, 'order_list': ]>, 'view': }
From here we can see the order "25" is actually in the response, i printed the username also and it matches.
I have tried this below mentioned code, but it didn't work for me.
class SeleniumTest(LiveServerTestCase):
#classmethod
def setUpClass(cls):
super().setUpClass()
cls.driver = PhantomJS()
#override_settings(DEBUG=True)
def test_queries(self)
with self.assertNumQueries(10):
self.driver.get(self.live_server_url + "/page-with-10-queries")
Output:
query['sql'] for query in self.captured_queries
AssertionError: 0 != 10 : 0 queries executed, 10 expected
Captured queries were:
To test the queries generated by a view, I use the following code:
def test_queries(self):
with self.assertNumQueries(3):
found = self.client.get(reverse('<url name>'))
print(found) # This is the line that initiates the Lazy query
The following is a not tested suggestion but if you insist on using selenium you may try:
#override_settings(DEBUG=True)
def test_queries(self)
with self.assertNumQueries(10):
found = self.driver.get(self.live_server_url + "/page-with-10-queries")
print(found) # Just an idea
Is it possible to create a integration test of a scrapy-pipeline? I can't figure out how to do this. In particular I am trying to write a test for the FilesPipeline and I also want it to persist my mocked response to Amazon S3.
Here is my test:
def _mocked_download_func(request, info):
return Response(url=response.url, status=200, body="test", request=request)
class FilesPipelineTests(unittest.TestCase):
def setUp(self):
self.settings = get_project_settings()
crawler = Crawler(self.settings)
crawler.configure()
self.pipeline = FilesPipeline.from_crawler(crawler)
self.pipeline.open_spider(None)
self.pipeline.download_func = _mocked_download_func
#defer.inlineCallbacks
def test_file_should_be_directly_available_from_s3_when_processed(self):
item = CrawlResult()
item['id'] = "test"
item['file_urls'] = ['http://localhost/test']
result = yield self.pipeline.process_item(item, None)
self.assertEquals(result['files'][0]['path'], "full/002338a87aab86c6b37ffa22100504ad1262f21b")
I always run into the following error:
DirtyReactorAggregateError: Reactor was unclean.
How do I create a proper test using twisted and scrapy?
Up do now I did my pipeline tests without the call to from_crawler, so they are not ideal, because they do not test the functionality of from_crawler, but they work.
I do them by using an empty Spider instance:
from scrapy.spiders import Spider
# some other imports for my own stuff and standard libs
#pytest.fixture
def mqtt_client():
client = mock.Mock()
return client
def test_mqtt_pipeline_does_return_item_after_process(mqtt_client):
spider = Spider(name='spider')
pipeline = MqttOutputPipeline(mqtt_client, 'dummy-namespace')
item = BasicItem()
item['url'] = 'http://example.com/'
item['source'] = 'dummy source'
ret = pipeline.process_item(item, spider)
assert ret is not None
(in fact, I forgot to call open_spider())
You can also have a look at how scrapy itself does the testing of pipelines, e.g. for MediaPipeline:
class BaseMediaPipelineTestCase(unittest.TestCase):
pipeline_class = MediaPipeline
settings = None
def setUp(self):
self.spider = Spider('media.com')
self.pipe = self.pipeline_class(download_func=_mocked_download_func,
settings=Settings(self.settings))
self.pipe.open_spider(self.spider)
self.info = self.pipe.spiderinfo
def test_default_media_to_download(self):
request = Request('http://url')
assert self.pipe.media_to_download(request, self.info) is None
You can also have a look through their other unit tests. For me, these are always good inspiration on how to unit test scrapy components.
If you want to test the from_crawler function, too, you could have a look on their Middleware tests. In these tests, they often use from_crawler to create middlewares, e.g. for OffsiteMiddleware.
from scrapy.spiders import Spider
from scrapy.utils.test import get_crawler
class TestOffsiteMiddleware(TestCase):
def setUp(self):
crawler = get_crawler(Spider)
self.spider = crawler._create_spider(**self._get_spiderargs())
self.mw = OffsiteMiddleware.from_crawler(crawler)
self.mw.spider_opened(self.spider)
I assume the key component here is to call get_crawler from scrapy.utils.test. Seems they factored out some calls you need to do in order to have a testing environment.
I'm newbie in Django tests. How to create Unit Test for this views function? My unit test function should import function from views? Please an example. This will help me to understand how it work
#maintainance_job
def time_to_end(request):
today = datetime.date.today()
datas = Data.objects.filter(start__lte=today,
other_date__gte=today)
for data in datas:
subject = _(u'Send email')
body = render_to_string('mail.txt',
{'data': data})
email = EmailMessage(subject, body,
'admin#admin.com',
[data.user.email])
email.send()
return HttpResponse('Done')
urls:
(r'^maintainance/jobs/time_to_end/$', 'content.views.time_to_end'),
There is a simpliest test for your case (place it in tests.py of a directory where is your view function):
from django.utils import unittest
from django.test.client import Client
class HttpTester( unittest.TestCase ):
def setUp( self ):
self._client = Client() # init a client for local access to pages of your site
def test_time_to_end( self ):
response = self._client.get( '/jobs/time_to_end/' )
# response = self._client.post( '/jobs/time_to_end/' ) - a 'POST' request
result = response.content
assert result != 'Done'
So, we use self._client to make 'get' and 'post' requests. Responses can be accessed by reading response.content (the full text of response) or by reading response.context if you use templates and want to access variables passing to the templates.
For example if your view normally must pass the dict with context variable 'result' to template:
{ 'result': "DONE" }
then you could check your result:
result = response.context[ 'result' ]
assert result != 'Done'
So, you wait your test will have the 'result' variable and it will be 'Done'. Otherwise you raise AssertionError (note assert statement).
If there is an exception then tests fails. AssertionError is an exception too.
More details - in the docs and in a book "Dive into Python".
I'm trying to test my webapp2 handlers. To do this, I thought it would be a good idea to send a request to the handler e.g.:
request = webapp2.Request.blank('/')
# Get a response for that request.
response = request.get_response(main.app)
The problem is, response is mostly just a bunch of HTML etc.
I want to look at what was passed to my jinja2 template from the handler before it was turned into HTML.
I want my test to get at the state within the handler class code. I wan't to be able to see what certain variables looked like in the response handler, and then I want to see what the dict templates looks like before it was passed to render_to_response()
I want to test these variables have the correct values.
Here is my test code so far, but I'm stuck because response = request.get_response() just gives me a bunch of html and not the raw variables.
import unittest
import main
import webapp2
class DemoTestCase(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def testNothing(self):
self.assertEqual(42, 21 + 21)
def testHomeHandler(self):
# Build a request object passing the URI path to be tested.
# You can also pass headers, query arguments etc.
request = webapp2.Request.blank('/')
# Get a response for that request.
response = request.get_response(main.app)
# Let's check if the response is correct.
self.assertEqual(response.status_int, 200)
self.assertEqual(response.body, 'Hello, world!')
if __name__ == '__main__':
unittest.main()
and here is my handler:
class HomeHandler(BaseHandler):
def get(self, file_name_filter=None, category_filter=None):
file_names = os.listdir('blog_posts')
blogs = []
get_line = lambda file_: file_.readline().strip().replace("<!--","").replace("-->","")
for fn in file_names:
with open('blog_posts/%s' % fn) as file_:
heading = get_line(file_)
link_name = get_line(file_)
category = get_line(file_)
date_ = datetime.strptime(fn.split("_")[0], "%Y%m%d")
blog_dict = {'date': date_, 'heading': heading,
'link_name': link_name,
'category': category,
'filename': fn.replace(".html", ""),
'raw_file_name': fn}
blogs.append(blog_dict)
categories = Counter(d['category'] for d in blogs)
templates = {'categories': categories,
'blogs': blogs,
'file_name_filter': file_name_filter,
'category_filter': category_filter}
assert(len(file_names) == len(set(d['link_name'] for d in blogs)))
self.render_template('home.html', **templates)
and here is my basehandler:
class BaseHandler(webapp2.RequestHandler):
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_template(self, filename, **kwargs):
#kwargs.update({})
#TODO() datastore caching here for caching of (handlername, handler parameters, changeable parameters, app_upload_date)
#TODO() write rendered page to its own html file, and just serve that whole file. (includes all posts). JQuery can show/hide posts.
self.response.write(self.jinja2.render_template(filename, **kwargs))
Perhaps I have got the wrong idea of how to do unit testing, or perhaps I should have written my code in a way that makes it easier to test? or is there some way of getting the state of my code?
Also if someone were to re-write the code and change the variable names, then the tests would break.
You can mock BaseHandler.render_template method and test its parameters.
See this question for a list of popular Python mocking frameworks.
Thanks to proppy's suggestion I ended up using a mock.
http://www.voidspace.org.uk/python/mock/
(mock is included as part or unittest.mock in python 3)
So here is my main.py code which is similar to what I have in webapp2:
note instead of BaseHandler.render_template i have BaseHandler.say_yo
__author__ = 'Robert'
print "hello from main"
class BaseHandler():
def say_yo(self,some_number=99):
print "yo"
return "sup"
class TheHandler(BaseHandler):
def get(self, my_number=42):
print "in TheHandler's get()"
print self.say_yo(my_number)
return "TheHandler's return string"
and atest.py:
__author__ = 'Robert'
import unittest
import main
from mock import patch
class DemoTestCase(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def testNothing(self):
self.assertEqual(42, 21 + 21)
def testSomeRequests(self):
print "hi"
bh = main.BaseHandler()
print bh.say_yo()
print "1111111"
with patch('main.BaseHandler.say_yo') as patched_bh:
print dir(patched_bh)
patched_bh.return_value = 'double_sup'
bh2 = main.BaseHandler()
print bh2.say_yo()
print "222222"
bh3 = main.BaseHandler()
print bh3.say_yo()
print "3333"
th = main.TheHandler()
print th.get()
print "44444"
with patch('main.BaseHandler.say_yo') as patched_bh:
patched_bh.return_value = 'last_sup'
th = main.TheHandler()
print th.get()
print th.get(123)
print "---"
print patched_bh.called
print patched_bh.call_args_list
print "555555"
if __name__ == '__main__':
unittest.main()
this code gives lots of output, here is a sample:
44444
in TheHandler's get()
last_sup
TheHandler's return string
in TheHandler's get()
last_sup
TheHandler's return string
---
True
[call(42), call(123)]
555555