Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force the race condition in transaction in django tests?

Is there a way to run the django tests using multiple threads and force the race condition? I want to make sure that the code path that handles the transaction errors is executed. To be slightly more specific, I want to be able to spawn 2 threads that will try to perform the same action on the database, with one of them succeeding and the other one failing. I am using the test framework that is in django.

Python pseudocode:

def some_method():
  try
    with transaction.atomic():
      objectA = get_object_from_db()
      objectA.delete()
  except Error:
    # error handling code to be run


class TestClass(TransactionalTestCase):
  def test_some_method():
    # run two threads and make sure that the race condition was present and some_method recovered successfully
like image 566
bialpio Avatar asked Mar 23 '14 06:03

bialpio


1 Answers

From what I'm reading you want to cover the path where you handle the exception. I'm asking you this question: do you really need it to be triggered in the case of multithreads race condition or do you just want to make sure that in the case it happens it does the right thing ?

Here's what I would do:

import unittest
import mock


# added just mimic django's orm for the purpose of the demo
class QuerySet(object):
  def delete(self):
    pass

def get_object_from_db():
  return QuerySet()

def some_method():
  try:
    objectA = get_object_from_db()
    objectA.delete()
    return True # this should be whatever you want to do in case it worked
  except Exception: # I would look up and check what ever error the django orm is raising.
    return False # this should be whatever you want to do in case it didn't work

class TestClass(unittest.TestCase):
  def test_some_method_in_case_it_worked(self):
    self.assertEqual(some_method(), True)

  def test_some_method_in_case_it_did_not_work(self):
    with mock.patch('__main__.get_object_from_db') as mocked_get_object_from_db:
      mocked_get_object_from_db.side_effect = RuntimeError('a message')
      self.assertEqual(some_method(), False)

if __name__ == '__main__':
    unittest.main()

mock is now part of the standard library. https://pypi.python.org/pypi/mock

Doing this is saving you from having flapper tests. You know the ones that randomly fail.

like image 194
jackdbernier Avatar answered Oct 15 '22 10:10

jackdbernier