FactoryBoy: how to teardown? - django

I don't understand how teardown in FactoryBoy + Django works.
I have a testcase like this:
class TestOptOutCountTestCase(TestCase):
multi_db = True
def setUp(self):
TestCase.setUp(self)
self.date = datetime.datetime.strptime('05Nov2014', '%d%b%Y')
OptoutFactory.create(p_id=1, cdate=self.date, email='inv1#test.de', optin=1)
def test_optouts2(self):
report = ReportOptOutsView()
result = report.get_optouts()
self.assertEqual(len(result), 1)
self.assertEqual(result[0][5], -1)
setUp is running once for all tests correct? Now if I had a second test and needed a clean state before running it, how do I achieve this? Thanks

If I understand you correctly you don't need tearDown in this case, as resetting the database between each test is the default behaviour for a TestCase.
See:
At the start of each test case, before setUp() is run, Django will flush the database, returning the database to the state it was in directly after migrate was called.
...
This flush/load procedure is repeated for each test in the test case, so you can be certain that the outcome of a test will not be affected by another test, or by the order of test execution.
Or do you mean to limit the creation of instances via the OutputFactory to certain tests?
Then you probably shouldn’t put the creation of instances into setUp.
Or you create two variants of your TestCase, one for all tests that rely on the factory and one for the ones that don't.
Regarding the uses of tearDown check this answer: Django when to use teardown method

Related

Django testing model instance setup

Why when creating instance as a class attribute:
class ModelTestsProducts(TestCase):
# Create example objects in the database
product_category = models.Product_category.objects.create(
name='Spring'
)
product_segment = models.Product_segment.objects.create(
name='PS1',
product_category=self.product_category
)
product_group = models.Product_group.objects.create(
name='PG1',
product_segment=self.product_segment
)
def test_product_category(self):
self.assertEqual(str(self.product_category), self.product_category.name)
def test_product_segment(self):
self.assertEqual(str(self.product_segment), self.product_segment.name)
def test_product_group(self):
self.assertEqual(str(self.product_group), self.product_group.name)
I am getting following error when running test for the 2nd time?
django.db.utils.IntegrityError: duplicate key value violates unique constraint "products_product_category_name_key"
DETAIL: Key (name)=(dadsad) already exists.
When I use setUp method and then create objects insite this setUp method it works fine, but I cant understand why the above method somehow creates every object multiple times and thus fails the unique constraint set in the model.
Is it because django test suite somehow calls this class everytime every test function is run, thus every attribute is assigned multiple times?
But then if I move the object assignment outside the class (in the test file) then I also get this duplicate error, so that would mean whole test file is being called multiple times every time test is being run.
One more thing I use docker to run this Django app and and run django test from docker-compose command.
If you're using Django's TestCase, Django will run each test in a separate transaction, that will be rolled back after the test is finished, which means there will be no changes in your database and everything you'll try to run, will only exist inside your test. This is done to make sure your tests are not affecting each other.
setUp function is also executed inside this transaction and it's invoked before every test in your test class. But everything you run outside of that function, in your case in the class body, will not be wrapped in such transaction, so it won't be rolled back between your tests. If this code is reached twice (which may be done under the hood by the test runner), your code will try to create some data in the database that already exists and it will fail.
If you want to do some optimizations of how your tests are being executed, you may use setUpTestData, so your test data is initialized only once for all tests in a single test class. It'll be wrapped in an outer-shell transaction and will be rolled back after all tests from such test case are done.

how to prevent records from commit in odoo unit testing

While writing test cases in Odoo 10 custom modules, I'm using self.env['test.model'].create({..}), this is actually committing the records in DB instead of getting rolled back after the completion of test cases. I used SingleTrasactionCase and TransactionCase to write my test cases.
Can someone suggest to me the cleanup code I can add to prevent this from happening?
Below is my sample code:
class TestModel(SingleTransactionCase):
def setUp(self:
super(TestModel, self).setUp()
self.testMod = self.env['test.model'].create({..})
def test_form_submit(self):
self.testMod.form_submit()
self.assertEquals(...)

Django unit tests fail when ran as a whole and there is GET call to the API

I am facing an issue when I run the tests of my django app with the command
python manage.py test app_name OR
python manage.py test
All the test cases where I am fetching some data by calling the GET API, they seem to fail because there is no data in the response in spite of there being in the test data. The structure which I have followed in my test suite is there is a base class of django rest framework's APITestCase and a set_up method which creates test objects of different models used in the APIs and I inherit this class in my app's test_views class for any particular API
such as
class BaseTest(APITestCase):
def set_up(self):
'''
create the test objects which can be accessed by the main test
class.
'''
self.person1= Person.objects.create(.......)
class SomeViewTestCase(BaseTest):
def setUp(self):
self.set_up()
def test_some_api(self):
url='/xyz/'
self.client.login(username='testusername3',password='testpassword3')
response=self.client.get(url,{'person_id':self.person3.id})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data),6)
So whenever I run the test as
./manage.py test abc.tests.test_views.SomeViewTestCase
it works fine, but when I run as
./manage.py test abc
The test above response.data has 0 entries and similarly, with the other tests within the same class the data is just not fetched and hence all the asserts fail.
How can I ensure the successful run of the test when they are run as a whole because during deployment they have to go through CI?
The versions of the packages and system configuration are as follows:
Django Version -1.6
Django Rest Framework - 3.1.1
Python -2.7
Operating System - Mac OS(Sierra)
Appreciate the help.Thanks.
Your test methods are executed in arbitrary order... And after each test, there's a tearDown() method that takes care to "rollback to initial state" so you have isolation between tests execution.
The only part that is shared among them is your setUp() method. that is invoked each time a test runs.
This means that if the runner start from the second test method and you only declare your response.data in your first test, all tests are gonna fail apart the posted one.
Hope it helps...

How do I run my Django testcase multiple times?

I want to perform some exhaustive testing against one of my test-cases (say, create a document, to debug some weird things I am encountering..)
My brutal force was to fire python manage.py test myapp in a loop either using Popen or os.system, but now I am back to pure way ?.....
def SimpleTest(unittest.TestCase):
def setUp(self):
def test_01(self):
def tearDown(self):
def suite():
suite = unittest.TestCase()
suite.add(SimpleTest("setUp"))
suite.add(SimpleTest("test_01"))
suite.add(SimpleTest("tearDown"))
return suite
def main():
for i in range(n):
suite().run("runTest")
I ran python manage.py test myapp and I got
File "/var/lib/system-webclient/webclient/apps/myapps/tests.py", line 46, in suite
suite = unittest.TestCase()
File "/usr/lib/python2.6/unittest.py", line 216, in __init__
(self.__class__, methodName)
ValueError: no such test method in <class 'unittest.TestCase'>: runTest
I've googled the error, but I still clueless (I was told to add an empty runTest method, but that doesn't sound right at all...)
Well, according to python's unittest.TestCase:
The simplest TestCase subclass will simply override the runTest()
method in order to perform specific testing code
As you can see, my whole goal is to run my SimpleTest N times. I need to keep track of pass, failure against N.
What option do I have?
Thanks.
Tracking race conditions via unit tests is tricky. Sometimes you're better off hitting your frontend with automated testing tool like Selenium -- unlike unit test, environment is the same and there's no need for extra work to ensure concurrency. Here's one way to run concurrent code in tests when there're no better option: http://www.caktusgroup.com/blog/2009/05/26/testing-django-views-for-concurrency-issues/
Just keep in mind that concurrent test is no definite proof you're free from race conditions -- there's no guarantee it'll recreate all possible combinations of execution order among processes.

Django testing - InternalError: current transaction is aborted, commands ignored until end of transaction block

In my tests I do not only test for the perfect case, but especially for edge cases and error conditions. So I wanted to ensure some uniqueness constraints work.
While my test and test fixtures are pretty complicated I was able to track the problem down to the following example, which does not use any custom models. To reproduce the behaviour just save the code into tests.py and run the django test runner.
from django.contrib.auth.models import User
from django.db import IntegrityError
from django.test import TransactionTestCase
class TransProblemTest(TransactionTestCase):
def test_uniqueness1(self):
User.objects.create_user(username='user1', email='user1#example.com', password='secret')
self.assertRaises(IntegrityError, lambda :
User.objects.create_user(username='user1', email='user1#example.com', password='secret'))
def test_uniqueness2(self):
User.objects.create_user(username='user1', email='user1#example.com', password='secret')
self.assertRaises(IntegrityError, lambda :
User.objects.create_user(username='user1', email='user1#example.com', password='secret'))
A test class with a single test method works, but fails with two identical method implementations.
The first test throwing exception breaks the Django testing environment and makes all the following tests fail.
I am using Django 1.1 with Ubuntu 10.04, Postgres 8.4 and psycopg2.
Does the problem still exist in Django 1.2?
Is it a known bug or am I missing something?
Django has two flavors of TestCase: "plain" TestCase and TransactionTestCase. The documentation has the following to say about the difference between the two of them:
TransactionTestCase and TestCase are identical except for the manner in which the database is reset to a known state and the ability for test code to test the effects of commit and rollback. A TransactionTestCase resets the database before the test runs by truncating all tables and reloading initial data. A TransactionTestCase may call commit and rollback and observe the effects of these calls on the database.
A TestCase, on the other hand, does not truncate tables and reload initial data at the beginning of a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test.
You are using TransactionTestCase to execute these tests. Switch to plain TestCase and you will see the problem vanish provided you maintain the existing test code.
Why does this happen? TestCase executes test methods inside a transaction block. Which means that each test method in your test class will be run inside a separate transaction rather than the same transaction. When the assertion (or rather the lambda inside) raises an error it dies with the transaction. The next test method is executed in a new transaction and therefore you don't see the error you've been getting.
However if you were to add another identical assertion in the same test method you would see the error again:
class TransProblemTest(django.test.TestCase):
def test_uniqueness1(self):
User.objects.create_user(username='user1', email='user1#example.com', password='secret')
self.assertRaises(IntegrityError, lambda :
User.objects.create_user(username='user1', email='user1#example.com', password='secret'))
# Repeat the test condition.
self.assertRaises(IntegrityError, lambda :
User.objects.create_user(username='user1', email='user1#example.com', password='secret'))
This is triggered because the first assertion will create an error that causes the transaction to abort. The second cannot therefore execute. Since both assertions happen inside the same test method a new transaction has not been initiated unlike the previous case.
Hope this helps.
I'm assuming when you say "a single test method works", you mean that it fails, raises the exception, but doesn't break the testing environment.
That said, you are running with AutoCommit turned off. In that mode, everything on the shared database connection is a single transaction by default and failures require the transaction to be aborted via ROLLBACK before a new one can be started. I recommend turning AutoCommit on, if possible--unless you have a need for wrapping multiple write operations into a single unit, having it off is overkill.