3

What is the best method for testing a method that looks like this

class Foo(object):

   is_running = False
   def run(self):
       self.is_running = True
       while self.is_running:
           do_some_work()

This is pretty standard code for a consumer, do work while the is_running flag is set.

But this is difficult to test because it's going to enter the loop and never come out unless I create a second thread to change is_running to false.

Are there any good strategies for testing this without spinning up a separate thread to run the code?

I haven't seen anything but I am thinking maybe the mock library would provide functionality that can return a [True, True, False] each time is_running is read but would that require me to change is_running from a member variable to a property or a method?

5
  • perhaps you can use subprocess.Popen.wait()... Commented Oct 7, 2014 at 19:06
  • Why not replace it with a full descriptor instead of just a property? Commented Oct 7, 2014 at 19:09
  • why not use coroutines: docs.python.org/3/library/asyncio-task.html Commented Oct 7, 2014 at 19:09
  • 2
    Try threading.Timer - it spins up the thread for you. This object looks like its supposed to run in a separate thread anyway, so unit testing it that way seems normal to me. Commented Oct 7, 2014 at 19:13
  • 1
    I'm with @tdelaney - if the class is designed to be used in conjunction with another thread, why not test it that way? Creating a thread to use for this sort of test would only take a few lines of code, so its not like it adds a ton of overhead. Commented Oct 7, 2014 at 19:15

1 Answer 1

3

As I mentioned in the comments, I think testing this method using threads is a perfectly viable approach, and probably the best solution. However, if you really want to avoid threads, you could convert is_running to a property and then use mock.PropertyMock to mock the property:

import mock
import time

class Foo(object):

   def __init__(self):
       self._is_running = False

   @property
   def is_running(self):
       return self._is_running

   @is_running.setter
   def is_running(self, val):
       self._is_running = val 

   def run(self):
       self._is_running = True  # Don't go through the property here.
       while self.is_running:
           print("in here")
           time.sleep(.5)


with mock.patch('__main__.Foo.is_running', new_callable=mock.PropertyMock,
                side_effect=[True, True, False]) as m:
    f = Foo()
    f.run()

Output:

in here
in here
<done>

I would say that changing your production implementation this much just to enable a specific method of testing is not worth it, though. Just have your test function create a thread to set is_running after some period of time.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.