Background: I am working on a web scraper to track prices at online stores. It uses Django. I have a module for each store, with functions like get_price()
and get_product_name()
written for each one, so that the modules can be used interchangeably by the main scraper module. I have store_a.py, store_b.py, store_c.py, et cetera, each with these functions defined.
In order to prevent duplication of code, I've made StoreTestCase, which inherits from TestCase. For each store, I have a subclass of StoreTestCase, like StoreATestCase and StoreBTestCase.
When I manually test the StoreATestCase class, the test runner does what I want. It uses the data in the child class self.data
for its tests, and doesn't attempt to set up and test the parent class on its own:
python manage.py test myproject.tests.test_store_a.StoreATest
However, when I manually test against the module, like:
python manage.py test myproject.tests.test_store_a
It first runs the tests for the child class and succeeds, but then it runs them for the parent class and returns the following error:
for page in self.data:
TypeError: 'NoneType' object is not iterable
store_test.py (parent class)
from django.test import TestCase
class StoreTestCase(TestCase):
def setUp(self):
'''This should never execute but it does when I test test_store_a'''
self.data = None
def test_get_price(self):
for page in self.data:
self.assertEqual(store_a.get_price(page['url']), page['expected_price'])
test_store_a.py (child class)
import store_a
from store_test import StoreTestCase
class StoreATestCase(StoreTestCase):
def setUp(self):
self.data = [{'url': 'http://www.foo.com/bar', 'expected_price': 7.99},
{'url': 'http://www.foo.com/baz', 'expected_price': 12.67}]
How do I ensure the Django test runner only tests the child class, and not the parent class?
The request factory The API for the RequestFactory is a slightly restricted subset of the test client API: It only has access to the HTTP methods get() , post() , put() , delete() , head() , options() , and trace() . These methods accept all the same arguments except for follow .
Writing testsDjango's unit tests use a Python standard library module: unittest . This module defines tests using a class-based approach.
from the docs: When using SQLite, the tests will use an in-memory database by default (i.e., the database will be created in memory, bypassing the filesystem entirely!).
django-nose provides all the goodness of nose in your Django tests, like: Testing just your apps by default, not all the standard ones that happen to be in INSTALLED_APPS. Running the tests in one or more specific modules (or apps, or classes, or folders, or just running a specific test)
One way to fix this is to use Mixins
:
from django.test import TestCase
class StoreTestCase(object):
def setUp(self):
'''This should never execute but it does when I test test_store_a'''
self.data = None
def test_get_price(self):
for page in self.data:
self.assertEqual(store_a.get_price(page['url']), page['expected_price'])
class StoreATestCase(StoreTestCase, TestCase):
def setUp(self):
self.data = [{'url': 'http://www.foo.com/bar', 'expected_price': 7.99},
{'url': 'http://www.foo.com/baz', 'expected_price': 12.67}]
The StoreTestCase
will not be executed since it is not a TestCase
, but your StoreATestCase
will still benefit from the inheritance.
I think that your issue happens because StoreTestCase
is a TestCase
instance, so it gets executed when you run the tests.
Edit:
I would also suggest to raise an exception in StoreTestCase.setUp
, explicitly saying that is not implemented. Have a look at these exception.
You would end up with something like this:
import exceptions # At the top of the file
[...]
def setUp(object):
raise exceptions.NotImplementedError('Please override this method in your subclass')
You can to hide base class inside another:
store_test.py (parent class)
from django.test import TestCase
class TestHelpers(object):
class StoreTestCase(TestCase):
...
test_store_a.py (child class)
import store_a
from store_test import TestHelpers
class StoreATestCase(TestHelpers.StoreTestCase):
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With