I use a multi-threaded design (had no choice), but most of my code resides in a single thread where all events in it are managed via a queue. In this fashion most of my code behaves as if it is single threaded, and I don't have to worry about locks, semaphores and what not.
Alas I've come to the point where I need to unittest my code (please don't lash for not TDDing in the first place), and I'm at a loss - how do you test something in another thread?
For instance, say I have the following class:
class MyClass():
def __init__(self):
self.a=0
# register event to self.on_event
def on_some_event(self, b):
self.a += b
def get(self):
return self.a
and I want to test:
import unittest
from queued_thread import ThreadedQueueHandler
class TestMyClass(unittest.TestCase):
def setUp(self):
# create the queued thread and assign the queue to self.queue
def test_MyClass(self):
mc = MyClass()
self.queue.put({'event_name':'some_event', 'val':1})
self.queue.put({'event_name':'some_event', 'val':2})
self.queue.put({'event_name':'some_event', 'val':3})
self.assertEqual(mc.get(),6)
if __name__ == '__main__':
unittest.main()
MyClass.get()
works fine for anything inside the queued thread, but it will be called asynchronously in the main thread by the test, thus the result may not be correct!
This plugin makes it possible to run tests quickly using multiprocessing (parallelism) and multithreading (concurrency).
Python doesn't support multi-threading because Python on the Cpython interpreter does not support true multi-core execution via multithreading. However, Python does have a threading library. The GIL does not prevent threading.
Python is NOT a single-threaded language. Python processes typically use a single thread because of the GIL. Despite the GIL, libraries that perform computationally heavy tasks like numpy, scipy and pytorch utilise C-based implementations under the hood, allowing the use of multiple cores.
The TensorFlow Session object is multithreaded, so multiple threads can easily use the same session and run ops in parallel.
If your design assumes everything must go through the queue, then don't fight it - make everything go through it!
Add an on_call
event to your queued event handler, and register to it the following function:
def on_call(self, callback):
callback()
then modify your test to:
def test_MyClass(self):
def threaded_test():
self.assertEqual(mc.get(),6)
mc = MyClass()
self.queue.put(1)
self.queue.put(2)
self.queue.put(3)
self.queue.put({'event_name':'call','val':threaded_test})
You can have a look at test_threading.py in the stdlib tests which does something similar to what you are trying to do. The basic idea is to protect a thread execution with mutex and semaphore so that the execution is complete before the test condition is asserted.
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