According to Unittest: Where to patch, you need to patch Thread from where it is used (or where it is looked up). In your function run_threads
, you are using __main__.Threads
instead of threading.Threads
because of how from threading import Thread
imports. Remove mock_thread.return_value = None
and now all your threads in run_threads
will be MagicMocks that perform no functionality.
Your next problem is mocking res_queue
in run_threads
. When patching it in test_run_threads
, you have no way of replacing the res_queue
with a different queue, because you just replaced all new instances of queue.Queue
with MagicMock
.
It's better to rewrite this function to be easier to test.
I would suggest breaking run_threads()
into two functions.
create_thread_list(args_list, res_queue):
will be used to create our list of threads. By separating this out, we can change args_list
to be whatever list of arguments we want to test.
def create_thread_list(args_list, res_queue):
thread_list = []
for val in args_list:
thread = Thread(target=lambda q, arg1: q.put(threaded_function(arg1)), args=(res_queue, val))
thread_list.append(thread)
return thread_list
run_threads_2(thread_list, res_queue):
will be used to start the threads.
def run_threads_2(thread_list, res_queue):
result_list = []
for th in thread_list:
th.start()
for th in thread_list:
th.join()
while not res_queue.empty():
result_list.append((res_queue.get()))
return result_list
By separating these out, you can pass whatever arguments you want to test for your threads.
Below are some examples of how I would test this now:
import queue
import time
from unittest.mock import patch
class MockThread2:
def __init__(self, name, result_q):
self.name = name
self.result_q = result_q
def start(self):
self.result_q.put(self.name)
def join(self):
pass
class TestMultiThreadedFunctions(unittest.TestCase):
def test_run_threads_2(self):
arg_list = ['A', 'B', 'C']
result_q = queue.Queue()
# Testing if created threads actually call the target function
# without actually calling the function.
with patch('__main__.threaded_function') as mock_function:
thread_list = create_thread_list(args_list=arg_list, res_queue=result_q)
run_threads_2(thread_list=thread_list, res_queue=result_q)
# Check if all threads ran
self.assertEqual(len(arg_list), mock_function.call_count)
arg_list = ['C', 'A', 'D', 'B', 'E']
result_q = queue.Queue()
# Using the threaded function, but just patching sleep
with patch('time.sleep') as mock_sleep:
thread_list = create_thread_list(args_list=arg_list, res_queue=result_q)
result_list = run_threads_2(thread_list=thread_list, res_queue=result_q)
self.assertListEqual(arg_list, result_list)
def test_run_with_alternate_threads(self):
# testing with MockThread and expecting nothing in the result_q
result_q = queue.Queue()
thread_list = [MockThread() for _ in range(5)]
expected_list = []
result_list = run_threads_2(thread_list=thread_list, res_queue=result_q)
self.assertListEqual(expected_list, result_list)
# testing with MockThread2
result_q = queue.Queue()
thread_list = [MockThread2(str(name), result_q) for name in range(5)]
expected_list = ['0', '1', '2', '3', '4']
result_list = run_threads_2(thread_list=thread_list, res_queue=result_q)
self.assertListEqual(expected_list, result_list)