10

I have written the function send_formatted_email which formats email subject and message then calls the send_email function in a separate module.

Now I have to test that send_formatted_email is calling send_email with the expected arguments. For this purpose I am trying to mock send_email using patch, but it is not getting mocked.

test.py

@patch('app.util.send_email')
def test_send_formatted_email(self, mock_send_email):
    mock_send_email.return_value = True
    response = send_formatted_email(self.comment, to_email)
    mock_send_email.call_args_list
    ....

views.py

def send_formatted_email(comment, to_email):
    ...
    message = comment.comment
    subject = 'Comment posted'
    from_email = comment.user.email
    ...
    return send_email(subject, message, to_email, from_email)

util.py

def send_email(subject, message, to, from):
    return requests.post(
        ...
    )

I even tried app.util.send_email = MagicMock(return_value=True) but this didn't work either. Any idea what I am doing wrong?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Afnan Nazir
  • 458
  • 2
  • 4
  • 13
  • 20
    You patch where a function is *used*, not where it's *defined*. Try `@patch('app.views.send_email')` – jonrsharpe Aug 14 '16 at 12:34
  • 1
    Possible duplicate of [How to mock a function defined in a separate Python module using mock's @patch](http://stackoverflow.com/questions/14654009/how-to-mock-a-function-defined-in-a-separate-python-module-using-mocks-patch) – jonrsharpe Aug 14 '16 at 12:35
  • @jonrsharpe Thank you it worked. – Afnan Nazir Aug 14 '16 at 12:51
  • In the light of your answer now i am able to patch where function is defined if i import module instead of function itself. – Afnan Nazir Aug 14 '16 at 12:59
  • Possible duplicate of [Python mock patch doesn't work as expected for public method](https://stackoverflow.com/questions/30987973/python-mock-patch-doesnt-work-as-expected-for-public-method) – funky-future May 24 '17 at 21:15

2 Answers2

4

Like jonrsharpe already mentioned there is already an answer under another question.

In my case I were not able to use one of the alternatives that are provided (reload or patching my own module).

But I now just importing the needed method right before usage:

def send_formatted_email(comment, to_email):
    ...
    message = comment.comment
    subject = 'Comment posted'
    from_email = comment.user.email
    ...
    from app.util import send_email
    return send_email(subject, message, to_email, from_email)

This will load the module method after you patched it.

Cons:

  • The import is executed before each method call.
Community
  • 1
  • 1
Eruvanos
  • 670
  • 7
  • 13
3

Try this:

import app.util

...

return util.send_email(subject, message, to_email, from_email)

or:

@patch('app.views.send_email')

...

return send_email(subject, message, to_email, from_email)
Suchy
  • 141
  • 1
  • 5