analyticsPierce
analyticsPierce

Reputation: 3025

Test that a function is called

I am writing tests in my app that will test whether a method was called. This is running in Python 3.4.3 and pytest-2.9.2. I am new to PyTest but very familiar with RSpec and Jasmine. I'm not sure how to setup the test so that it will test that imaplib.IMAP4_SSL is called.

My app structure:

/myApp
  __init__.py
  /shared
    __init__.py
    email_handler.py
  /tests
    __init__.py
    test_email_handler.py

email_handler.py

import imaplib
def email_conn(host):
    mail = imaplib.IMAP4_SSL(host)  
    return mail;

What I have so far for my test: test_email_handler.py

import sys   
sys.path.append('.')  

from shared import email_handler 

def test_email_handler():   
     email_handler.email_conn.imaplib.IMAP4_SSL.assert_called_once 

This obviously fails. How can I setup this test so that it tests if imaplib.IMAP4_SSL is called? Or is there a better way to setup the test suite in my app so this will support testing more effectively?

Upvotes: 4

Views: 3895

Answers (3)

Lex Scarisbrick
Lex Scarisbrick

Reputation: 1570

Here's an example using unittest.mock from the Python 3.5.2 standard library:

test_email_handler.py

import sys
from unittest import mock
sys.path.append('.')

from shared import email_handler

@mock.patch.object(email_handler.imaplib, 'IMAP4_SSL')
def test_email_handler(mock_IMAP4_SSL):
    host = 'somefakehost'
    email_handler.email_conn(host)
    mock_IMAP4_SSL.assert_called_once_with(host)

Note the @mock.patch.object decorator that replaces IMAP4_SSL with a mock object, which is added as an argument. Mock is a powerful tool for testing that can be quite confusing for new users. I recommend the following for further reading:

https://www.toptal.com/python/an-introduction-to-mocking-in-python

http://engineroom.trackmaven.com/blog/mocking-mistakes/

http://alexmarandon.com/articles/python_mock_gotchas/

Upvotes: 3

Dmitry Tokarev
Dmitry Tokarev

Reputation: 2089

This sounds like a question of code coverage: was this line executed?

For python coverage tool is: https://coverage.readthedocs.io

Pytest built plugin based on that tool, which is very convenient: https://pypi.python.org/pypi/pytest-cov

Upvotes: 0

Marc Schmitt
Marc Schmitt

Reputation: 1499

what you can do is this :

email_handler.py

import imaplib

def email_conn(host):
    print("We are in email_conn()")
    mail = imaplib.IMAP4_SSL(host)
    print(mail)
    return mail;

test_email_handler.py

import sys   
sys.path.append('.')  

from shared import email_handler 

def test_email_handler():
    print("We are in test_email_handler()")
    email_handler.email_conn.imaplib.IMAP4_SSL.assert_called_once
    print(email_handler.email_conn.imaplib.IMAP4_SSL.assert_called_once) # this will give you the result of the function (in theory) 

Basically, what you do is printing what the functions returns. If there is no error, the function should have been executed.

What you could also do, is modifying the source code of imaplib in order to put a print inside the function you're calling.

Good luck !

Upvotes: 0

Related Questions