[PYTHON] How to Mock a Public function in Pytest

Hello, is around this time that the "roasted green tea latte taste" protein of a certain M ○ Protein bought in sale not drink at soup ** like a taste of ** shrimp (what

By the way, based on the policy that the API of our Django server ** covers 120% in the test **, we write the test code about twice as much as the implementation code every day. Under such circumstances, today I will briefly introduce "how to test the Public function implemented in the Helper class via API (and where it got stuck".

Implementation content you want to test

The following two functions have been implemented (function names etc. are dummy for posting, and setting.py and urls.py are already set separately)

app/views.py


from rest_framework.views import APIView
from app.helper import helper_foo
from app.models import HogehogeSerializer


class HogehogeListAPIView(APIView):
    """Implement Post functionality in API View using Django framework
    """
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, format=None):
    """ API Post/hogehoge
       """
        serializer = HogehogeSerializer(data=request.data)
        if serializer.is_valid(): #Checking Request data
            try:
                with transaction.atomic():
                    serializer.save()
                    helper_foo(serializer.data) #Call a Helper function
            except Exception as error: 
                return Response(status=status.HTTP_400_BAD_REQUEST)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

app/helper.py


import boto3

def helper_foo(data):
    """Helper function
Create an S3 Bucket based on the data
    """
    client = boto3.client("s")
    
    """
Create a bucket (throw an error if it fails)
    """
  client.create_bucket(...

And here is the content of pytest

app/tests.py


from django.test import TestCase
from django.conf import settings

from rest_framework.test import APIClient

class HogehogeAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.data = #Create test data

    def test_hogehoge_post(self):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Check with various Asserts

Test issues

The code that actually creates the S3Bucket is executed in the test, and every time the test is run, the extra S3Bucket increases.

Mock the Helper function with a patch

Executed by being able to mock the function as follows with patch in the Mock module of Unittest

Target specifies ʻapp.helper.foo_helper`.

app/tests.py


from unittest.mock import patch
from django.test import TestCase
from django.conf import settings

from rest_framework.test import APIClient

class HogehogeAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()

    @patch('app.helper.helper_foo')
    def test_hogehoge_post(self, mock_function):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Check with various Asserts
        self.assertEqual(mock_function.call_count, 1) #Check if the Mock function has been called once

When executed, the test result fails and the following message is displayed

self.assertEqual(mock_function.call_count, 1)
AssertionError: 0 != 1

Oh, isn't the function Mocked? Looking at the AWS console, S3Bucket is (unfortunately) well done.

What happened

After investigating various things, it is said that the reference logic of Patch is as follows.

Now we want to test some_function but we want to mock out SomeClass using patch (). The problem is that when we import module b, which we will have to do then it imports SomeClass from module a. If we use patch () to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect. (Source 3 / library / unittest.mock.html # where-to-patch))

In other words When module b is imported into module a, module b is referenced from module a, so referencing it with b.someclass (helper.helper_foo here) does not affect the test. And that

So, I changed the target of Mock from ʻapp.helper.foo_helper to ʻapp.views.foo_helper.

app/tests.py


from unittest.mock import patch
from django.test import TestCase
from django.conf import settings

from rest_framework.test import APIClient

class HogehogeAPITest(TestCase):
    def setUp(self):
        self.client = APIClient()

    @patch('app.views.helper_foo')
    def test_hogehoge_post(self, mock_function):
        response = self.client.post('/hogehoge',
                                    self.data},
                                    format='json')
        self.assertEqual(response.status_code, 201)
        #Check with various Asserts
        self.assertEqual(mock_function.call_count, 1) #Check if the Mock function has been called once

This confirmed that the test worked and that no S3 bucket was generated.

I've been using Python for almost 10 years, but for the first time I was able to understand how Module reference works when importing.

that's all. Test code is interesting in fields where there are various discoveries, so I'm hoping that more Pytest articles will be added to Qiita these days.

Recommended Posts

How to Mock a Public function in Pytest
How to call a function
How to make a recursive function
How to clear tuples in a list (Python)
To execute a Python enumerate function in JavaScript
How to embed a variable in a python string
How to create a function object from a string
How to create a JSON file in Python
How to implement a gradient picker in Houdini
How to notify a Discord channel in Python
[Python] How to draw a histogram in Matplotlib
How to write a named tuple document in 2020
How to count numbers in a specific range
[Go] How to write or call a function
How to read a file in a different directory
How to set up public key authentication in ssh
How to convert / restore a string with [] in python
How to define Decorator and Decomaker in one function
[Python] How to expand variables in a character string
A memorandum on how to use keras.preprocessing.image in Keras
How to install Google Test / Google Mock in Visual Studio 2019
How to reference static files in a Django project
[Python] How to call a c function from python (ctypes)
[Linux] How to put your IP in a variable
Covector to think in function
Create a function in Python
How to hack a terminal
How to develop in Python
How to unit test a function containing the current time using freezegun in python
How to import NoteBook as a module in Jupyter (IPython)
How to output a document in pdf format with Sphinx
A story about how to specify a relative path in python.
How to use the __call__ method in a Python class
How to import a file anywhere you like in Python
How to specify a public directory Python simple HTTP server
How to print characters as a table with Python's print function
How to generate a public key from an SSH private key
A handy function to add a column anywhere in a Pandas DataFrame
A note on how to load a virtual environment in PyCharm
I tried "How to get a method decorated in Python"
How to generate a query using the IN operator in Django
How to sample from any probability density function in Python
To return char * in a callback function using ctypes in Python
How to get a list of built-in exceptions in python
Let's create a function to hold down Button in Tkinter
How to import NoteBook as a module in Jupyter (IPython)
How to get a quadratic array of squares in a spiral!
How to make a Japanese-English translation
How to write a Python class
[Python] How to do PCA in Python
How to use the zip function
How to put a symbolic link
How to use classes in Theano
How to write soberly in pandas
How to collect images in Python
Mock in python-how to use mox
How to update Spyder in Anaconda
How to use SQLite in Python
How to create a Conda package
How to make a crawler --Advanced
How to create a virtual bridge