Search code examples
pythondjangopytestpytest-djangopython-unittest.mock

ModuleNotFoundError when trying to use mock.patch on a method


My pytest unit test keeps returning the error ModuleNotFoundError: No module name billing.

Oddly enough the send_invoices method in the billing module is able to be called when I remove the patch statement. Why is mock.patch unable to find the billing module and patch the method if this is the case?

billing.py

import pdfkit
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from projectxapp.models import User

Class Billing:

  #creates a pdf of invoice. Takes an invoice dictionary
  def create_invoice_pdf(self, invoice, user_id):
    #get the html of the invoice
    file_path ='/{}-{}.pdf'.format(user_id, invoice['billing_period'])
    invoice_html = render_to_string('templates/invoice.html', invoice)
    pdf = pdfkit.from_file(invoice_html, file_path)
    return file_path, pdf

  #sends invoice to customer
  def send_invoices(self, invoices):
    for user_id, invoice in invoices.items():
            #get customers email
            email = User.objects.get(id=user_id).email
            billing_period = invoice['billing_period']
            invoice_pdf = self.create_invoice_pdf(invoice, user_id)
            email = EmailMessage(
                'Statement of credit: {}-{}'.format(user_id, billing_period),
                'Attached is your statement of credit.\
                This may also be viewed on your dashboard when you login.',
                '[email protected]',
                [email],
            ).attach(invoice_pdf)

            email.send(fail_silently=False)

        return True

test.py

from mock import patch
from projectxapp import billing

@pytest.mark.django_db
def test_send_invoice():
  invoices = {
    1: {
        'transaction_processing_fee': 2.00,
        'service_fee': 10.00,
        'billing_period': '2020-01-02'
    },
    2: {
        'transaction_processing_fee': 4.00,
        'service_fee': 20.00,
        'billing_period': '2020-01-02'
    }
 }

  with patch('services.billing.Billing().create_invoice_pdf') as p1:
    p1.return_value = '/invoice.pdf'
    test = billing.Billing().send_invoices(invoices)

  assert test == True

Solution

  • Solution

    Since I had already imported the module of the method I needed to patch. I didn't need to use the full path including the package name.

    Changed

    patch('projectxapp.billing.Billing.create_invoice_pdf')

    to this

    patch('billing.Billing.create_invoice_pdf')

    From the unittest documentation:

    target should be a string in the form 'package.module.ClassName'. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are calling patch() from. The target is imported when the decorated function is executed, not at decoration time.