Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Side Effects: Advanced Test (Part 2)

00:00 So now that we have this kind of helper function, this .log_request() function within our test class, let’s write a test to test the response with a log statement.

00:13 We’ll make another function called def test_request_with_logging().

00:19 Just a side note here, the unittest module when we run unittest.main()it will look for the TestGetHolidays class and it will run functions that start with test_.

00:34 We want to make a GET request to our API, so we’re going to say requests.get.side_effect, and here instead of passing an exception or something, we’re going to pass this .log_request() function.

00:51 And rather, this is a self.log_request(), because it’s inside the class.

00:57 So this is a little magical. First off, remember that requests is not the actual requests module—it is a Mock object.

01:08 We’ve mocked requests and therefore .get() is going to be a Mock object and then .side_effect is this function.

01:18 So one tricky thing here is that when we set the .side_effect to a function, it’s going to inherit its arguments and its return value. So in other words, when we say requests.get(), remember that originally, the arguments of requests.get() is going to be this URL.

01:45 So since requests is a Mock object, we’re saying rwhich is requests.get()—this argument. So down in our test here, we already have access to that URL endpoint,

02:02 and that is going to be forwarded to this function since we’ve defined it as the .side_effect. So in a way, it kind of looks like we’re calling this function with this 'localhost/api/holidays'.

02:21 And I know it’s a little confusing, but it’s just something that you have to know and kind of roll with, is that requests.get()which we’ve set up here in our get_holidays() function—has this argument passed in into it, so to speak.

02:40 So when we make that GET request inside get_holidays(), we’re setting the .side_effect to this function, our .log_request() function.

02:55 So that means we can do something like assert get_holidays()so we call that function—and this should now have a response. So we’re mocking the request and we have the .status_code at 200, so that means that get_holidays() is going to drop into this block and return .json(), which we have mocked here—we’ve mocked the .return_value as this dictionary.

03:23 So we should be able to assert that get_holidays() is going to return a dictionary, and let’s check that the 25th of December is equal to 'Christmas'.

03:36 Let’s see if this actually works now. I’m going to open up a terminal and we will run our tests. Since we have this unittest.main(), it should pick up these three tests that start with test_. In the previous video, we already saw that these should pass, so let’s go ahead and cross our fingers and see if our new test passes.

03:58 So we’ll say python run my_calendar.py.

04:04 All right, it passed! It Ran 3 tests and we also see our log statement here.

Avatar image for artemudovyk

artemudovyk on April 13, 2022

It’s was a little bit hard to understand why we should use side_effect instead of return_value.

Found this answer that made it clear:

With side_effect you define a function/class (or iterator or exception), which should be called instead of the original function/class. With return_value you define the result, what the function/class is supposed to return so that we don’t need to call it.`

Avatar image for Jakub Urbánek

Jakub Urbánek on Nov. 25, 2022

For some reason my code is not working. Double checked twice, should be exactly the same, but doesn’t work. Is there any file to download with the whole code?

Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on Nov. 29, 2022

@Jakub Urbánek Yes, you can click on Supporting Materials | Sample Code from the dropdown just below the video, or grab this link directly: realpython.com/courses/python-mock-object-library/downloads/code-mock/

Avatar image for Andrew

Andrew on Dec. 14, 2024

I think I’ve been following along but get hit with an error

ERROR: test_request_with_logging (__main__.TestGetHolidays.test_request_with_logging)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/andrew/RealPython/Mock/01/my_calendar.py", line 51, in test_request_with_logging
    assert get_holidays()['25/12'] == 'Christmas'
           ^^^^^^^^^^^^^^
  File "/home/andrew/RealPython/Mock/01/my_calendar.py", line 24, in get_holidays
    if r.status_code == 200:
       ^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'status_code'

I’ve done a pip install requests as it didn’t exist in my python?

Full code

from unittest.mock import Mock
from datetime import datetime
import requests
import unittest
from requests.exceptions import ConnectionError, Timeout

tue = datetime(year=2019, month=1, day=1)
sat = datetime(year=2019, month=1, day=5)

datetime = Mock()
# print(dir(datetime))
def is_weekday():
    today = datetime.today()
    day_of_the_week = today.weekday()
    return (0 <= day_of_the_week < 5)

datetime.today.return_value = sat
assert not is_weekday()

requests = Mock()

def get_holidays():
    r = requests.get('http://localhost/api/holidays')
    if r.status_code == 200:
        return r.json()
    return None

class TestGetHolidays(unittest.TestCase):

    def test_get_holidays_connection(self):
        requests.get.side_effect = ConnectionError
        with self.assertRaises(ConnectionError):
            get_holidays()

    def test_get_holidays_timeout(self):
        requests.get.side_effect = Timeout
        with self.assertRaises(Timeout):
            get_holidays()

    def log_request(self, url):
        print(f'making a logging request to {url}')
        response_mock = Mock()
        response_mock.status_code = 200
        response_mock.json.return_value = {
            '25/12':'Christmas',
            '1/1':'New Years'
        }

    def test_request_with_logging(self):
        requests.get.side_effect = self.log_request
        assert get_holidays()['25/12'] == 'Christmas'

if __name__ == '__main__':
    unittest.main()

Become a Member to join the conversation.