Martiusweb/asynctest

patch abnormalities

ja8zyjits opened this issue · 3 comments

The problem

while patching and sub classing there seems to be a deviation from the normal unittests behaviour.

scritp.py

def run_something():
    pass

test_script.py

import asynctest
import asyncio
import unittest
import script

class MainTest(asynctest.TestCase):
    def setUp(self):
        pass

    @asynctest.patch("script.run_something")
    async def test_main(self, mock_run):
        script.run_something()
        self.assertEqual(mock_run.call_count, 1)

class MainTest2(MainTest):
    def setUp(self):
        pass

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

while running the code with python -m unittest iam gettting the following error

.F
======================================================================
FAIL: test_main (test_script.MainTest2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jzy/work/asynctest_issue_virtual/lib/python3.6/site-packages/asynctest/case.py", line 297, in run
    self._run_test_method(testMethod)
  File "/home/jzy/work/asynctest_issue_virtual/lib/python3.6/site-packages/asynctest/case.py", line 354, in _run_test_method
    self.loop.run_until_complete(result)
  File "/home/jzy/work/asynctest_issue_virtual/lib/python3.6/site-packages/asynctest/case.py", line 224, in wrapper
    return method(*args, **kwargs)
  File "/usr/lib/python3.6/asyncio/base_events.py", line 473, in run_until_complete
    return future.result()
  File "/home/jzy/work/asynctest_issue_virtual/lib/python3.6/site-packages/asynctest/_awaitable.py", line 21, in wrapper
    return await coroutine(*args, **kwargs)
  File "/home/jzy/work/asynctest_issue_virtual/lib/python3.6/site-packages/asynctest/mock.py", line 1006, in __next__
    return self.gen.send(None)
  File "/home/jzy/work/asynctest_issue_virtual/scripts/test_script.py", line 13, in test_main
    self.assertEqual(mock_run.call_count, 1)
AssertionError: 2 != 1

----------------------------------------------------------------------
Ran 2 tests in 0.002s

FAILED (failures=1)

But the same code in unittest passes.

test_script.py

import asynctest
import asyncio
import unittest
import script

class MainTest(asynctest.TestCase):
    def setUp(self):
        pass

    @unittest.mock.patch("script.run_something")
    def test_main(self, mock_run):
        script.run_something()
        self.assertEqual(mock_run.call_count, 1)

class MainTest2(MainTest):
    def setUp(self):
        pass

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

while running the code with python -m unittest iam gettting the following output

..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

The workaround

Iam able to get around this issue by using the context managers instead of the decorators like

with asynctest.patch("script.run_something") as mock_something:

And the test succeeds

Support information

  • python version 3.6.7
  • asynctest version
Name: asynctest
Version: 0.12.4
Summary: Enhance the standard unittest package with features for testing asyncio libraries
Home-page: https://github.com/Martiusweb/asynctest/
Author: Martin Richard
Author-email: martius@martiusweb.net
License: Apache 2
Location: /home/jzy/work/asynctest_issue_virtual/lib/python3.6/site-packages

The question

  • Is this an intended behavior?

Hi,

Thanks for your report.

You're running the same decorated test method twice, and it re-uses the same mock object, which is indeed a bug since unittest issues a new mock object every time.

I'll work on it.

@Martiusweb Thanks for your prompt response.

In fact, this issue raises other problems happening with concurrent executions of patched coroutines.

In particular, since several coroutines instances can live simultaneously, several instances of a patched coroutine will break each other.

The issue is not about re-using the same mock, but using the same patch for each coroutine instance. I'm not sure yet how to solve this: should the patchings be tied to the coroutine instance instead of the coroutine function?

The internal behavior of asynctest will likely change quite a lot, so I think it should not be released in a patch version. I'll merge the deprecation of python 3.4 first.