Loading video player…

Associated Attributes

00:00 Once again, here are the methods and attributes associated with the Mock objects. Let’s actually explore a couple more of these. I really like the way that they’re named because they’re kind of self-explanatory, what they do. For example, 'call_args'let’s look at this one. I’m going to delete this print() line and let’s say print(json.dumps.call_args).

00:25 And this one’s actually an attribute, not a method, so we’re just going to print this attribute of this json.dumps Mock object.

00:34 So go down and clear the screen, run the program,

00:39 and we’ll see this call({'a': 1}). So as you may have expected, it prints out the arguments that were called. This can be super useful when you’re running tests and you have a lot of complexity and sometimes the arguments change when you don’t expect them, so this will give you information of what the arguments actually were when the function was called. Another method, another useful tool with Mock objects is we have this .call_count attribute.

01:08 As you can probably guess, when we run this we’ll see 1. This is the number of times the method—or really, the Mock object—has been called.

01:19 And if we call this three times and we run our program again, then we’ll see 3. Another method we can use is called .method_calls.

01:34 So let’s print the return value of this .method_calls method.

01:41 This is an empty list. This one’s a little tricky, in my opinion. It actually works recursively, so if we were using more methods of the json module, this would be useful to show a list of all the methods that we have called.

01:58 And just to kind of clarify that: if we print json.method_calls instead of json.dumps, let’s see how that will change the output.

02:13 Now we can start to see how this is useful. We get this list of call.dumps({'a': 1}) and that repeats three times, and that represents all these calls up here on lines 5 through 7.

02:26 So if we had used, say, another method of the json module, maybe json.loads(), and we passed in some dictionary string, and then let’s run our program again—

02:41 and now we’ll see this list includes this method call as well, this new .loads() call. So .method_calls acts recursively and it shows all the different calls that you used from the, kind of, base—if you will—base Mock object.

02:57 Now you’ve learned some methods you can use to analyze and provide introspection into how your functions are being called, what arguments they’re being called with, how many times they’re being called, and you can use this information to gain insight into your program.

I’m having a real hard time to grasp why the methods showed would be even remotely useful.

You seem to be just checking how many times methods of the MOCKED object was called. How would that help anyone? For something like this to be helpful, perhaps one would want to count how many times the REAL ‘dumps’ or ‘loads’ was called is a code base? But counting how many times you own test-code called it? What’s even the point of that?

Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on Jan. 11, 2023

@al These are all valid questions, especially when you don’t have the whole picture. You’re absolutely right there’s little point in testing a mock object like in the example. The attributes shown in the video become useful when you temporarily substitute a real object somewhere in your code with a mocked one through dependency injection or monkey patching. The rest of your code won’t even know that it’s communicating with a fake object instead of the real deal as long as both objects have the same interface.

The point of mocking is to remove irrelevant or heavyweight dependencies from a piece of code under test. You certainly don’t want to manipulate a database only to unit test a signup form, for example. By injecting a mocked database, you’ll be able to run your test without actually connecting to a real database.

Here’s an example demonstrating this in pseudo-code:

# Hypothetical code under test:

class Database:
    def execute_query(self, sql):
        ...

class SignupForm:
    def __init__(self, database):
        self.database = database

    def signup(self, username):
        sql = f"INSERT INTO users (username) VALUES ('{username}')"
        self.database.execute_query(sql)
# Unit test code:

import unittest
from unittest.mock import Mock

class TestSignupForm(unittest.TestCase):
    def test_create_new_user(self):
        mock_database = Mock(Database)
        form = SignupForm(mock_database)
        form.signup("Bob")
        mock_database.execute_query.assert_called_with(
            "INSERT INTO users (username) VALUES ('Bob')"
        )

The mock object allows you to test the behavior of your code under test, i.e., whether it correctly called the dependencies that it was supposed to.

Become a Member to join the conversation.