google-code-export / pymox

Automatically exported from code.google.com/p/pymox
0 stars 0 forks source link

ExpectedMethodCallsError problems with async code (twisted) #13

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Hello,
while i like Mox, I've encountered a small issue when testing async twisted
code running trial (the twisted builtin test tool, supporting different
reactors, output, debugging, etc).

Since twisted wraps exceptions into failures, it may happen that, in order:

- VerifyAll() is called in a test method, but a certain expected method is
not called;
- a deferred is returned by the same method;
- the deferred then triggers a callback that calls the expected method:

this is what the trial output look like:

===============================================================================
[ERROR]: test_mox_async.AsyncMoxTestCase.testAsyncMox

Traceback (most recent call last):
  File
"/home/alan/Private/Dropbox/code/mox-0.5.1.fixtraceback/test_mox_async.py",
line 24, in testAsyncMox
    m.VerifyAll()
  File "/home/alan/Private/Dropbox/code/mox-0.5.1.fixtraceback/mox.py",
line 198, in VerifyAll
    mock_obj._Verify()
  File "/home/alan/Private/Dropbox/code/mox-0.5.1.fixtraceback/mox.py",
line 353, in _Verify
    raise ExpectedMethodCallsError(self._expected_calls_queue)
mox.ExpectedMethodCallsError: Verify: Expected methods never called:

The error is raised but, since the call has happened (too late, e.g. after
VerifyAll(), but it has happened), I can't see what the problem really is.

you can see the problem by yourself by running the attached file on a
system with twisted 8.2 installed:

trial test_mox_async.py 

This seems to be caused by the fact that the exception instance saves the
call queue and concatenates the string to be shown just when __str__ is
called (and that's too late when using trial and twisted).

The attached patch saves formatted calls on init, preventing further issues.

output on a patched Mox:

===============================================================================
[ERROR]: test_mox_async.AsyncMoxTestCase.testAsyncMox

Traceback (most recent call last):
  File
"/home/alan/Private/Dropbox/code/mox-0.5.1.fixtraceback/test_mox_async.py",
line 24, in testAsyncMox
    m.VerifyAll()
  File "/home/alan/Private/Dropbox/code/mox-0.5.1.fixtraceback/mox.py",
line 198, in VerifyAll
    mock_obj._Verify()
  File "/home/alan/Private/Dropbox/code/mox-0.5.1.fixtraceback/mox.py",
line 353, in _Verify
    raise ExpectedMethodCallsError(self._expected_calls_queue)
mox.ExpectedMethodCallsError: Verify: Expected methods never called:
  0.  __call__('value') -> None
-------------------------------------------------------------------------------
Ran 1 tests in 0.007s

FAILED (errors=1)

Original issue reported on code.google.com by alan.fra...@gmail.com on 9 Oct 2009 at 4:22

Attachments:

GoogleCodeExporter commented 9 years ago
Your patch doesn't really solve the race condition, since your callback could 
run between initializing Error, and setting up the local copy of 'calls'.

In this case, it seems like your test should block until you know your code has 
completed (or should have completed) before verifying.

Making mox thread-safe is a larger issue.

Sorry for the huge delay on this bug.  I'm trying to work through the backlog 
today.

Original comment by steve.mi...@gmail.com on 17 Jun 2010 at 8:07

GoogleCodeExporter commented 9 years ago
You're wrong here. Twisted is single thread - it uses a reactor. The callback 
can't run while the object is being created. I've never spoke about 
multithreading - it's a totally different matter.

Original comment by alan.fra...@gmail.com on 18 Jun 2010 at 10:39

GoogleCodeExporter commented 9 years ago
I'm not familiar with twisted or reactor, but I'm pretty sure that you code is 
using threads.   Maybe I'm confused, but I'd be *very* interested to see a call 
stack where your callback runs between exception creation and raising without a 
context switch.

Original comment by stev...@google.com on 18 Jun 2010 at 4:26

GoogleCodeExporter commented 9 years ago
http://en.wikipedia.org/wiki/Reactor_pattern

I think you missed the point, or I wasn't very clear.

The exception is raised indeed at the proper point. But twisted own's TestCase, 
being designed for working in async, does not raise the exception *immediately* 
; it uses a concept called "Failure" 

http://twistedmatrix.com/documents/8.1.0/api/twisted.python.failure.Failure.html

Which offers a way to propagate an exception through a chain of callbacks.

http://diigo.com/0bg0q

The exception is not shown as soon as it's raised - which would make the 
exception show up indeed! Instead, it gets wrapped in a failure and shown a bit 
later - in this time, the call gets done and the call gets cleared.

Original comment by alan.fra...@gmail.com on 18 Jun 2010 at 5:42