Loading video player…

Replacing Code

00:00 In the previous lesson, I introduced you to the Mock class in the unittest.mock module. In this lesson, I’ll show you how to configure Mock to act like the code it is replacing.

00:11 Previously, I showed you how to instantiate a Mock object, and how to assert what was or was not done to it. So far though, you’ve only seen Mock methods return other Mock objects.

00:22 When faking out real code, you’ll likely need the mocked methods to return something like your code does. You want the mock method to look like it behaves like the real one, returning values or raising exceptions as is appropriate.

00:36 The Mock objects provide ways to configure all of this. There are two common ways of using a Mock object. The first is to create an object and pass it into your code.

00:47 Your code then operates on the object, and your tests can then assert that the appropriate calls got made. There’s an object-oriented coding pattern known as dependency injection.

00:57 This pattern has you pass required objects into a function rather than have them created in the function itself. For example, you would pass a reference to an open file object rather than passing in a filename for the function to open itself.

01:11 This pattern is more common in statically typed languages than in Python, but you will see it used in some frameworks. If your code is structured this way, the dependency that you inject could be a Mock object when you’re testing.

01:26 The second common use of Mock is to dynamically patch the code that you call, replacing a module or object underneath with a mocked version. In this lesson, I’ll stick with the former, and in the later lessons, I’ll show you the latter.

01:39 You can’t typically use this dynamic mechanism in statically typed languages. Hence why you don’t see dependency injection in Python as much, as there are other ways of solving the same problem.

01:50 Alright, back to the REPL, this time to fake out some code. Before I use Mock, let me write a bit of code to mimic. The Path class from the pathlib module is used for interacting with file paths.

02:04 It’s a better abstraction than just a filename, as it’s aware of things like directories, and you can do operations on it.

02:13 Here, I’ve created a new Path object that references a file named message.txt. Note that this file doesn’t have to exist. You could use the path to create it.

02:23 In this case though, I have one. I’ve provided it in the sample code, but you could just as easily create it yourself. It only contains a single line of text.

02:32 The .read_text() method on a Path object opens the corresponding file, interprets its contents as text, and returns that text as a string.

02:42 If there was no message.txt file, this would’ve resulted in an exception. Now that you’ve seen how this works, let me write a little function that uses this idea.

02:52 The reader() function takes an argument that it assumes is a Path object. Then it calls .read_text() on that object, and prints out the contents of the file and the length of the content.

03:08 If I pass in our previously created message path, these are the results I get. Let’s see what’s involved in faking out a Path object.

03:19 First, you start by importing Mock. You know how at the state fair, there’s sometimes a contest where you guess how many jelly beans there are in a jar?

03:29 Well, as a complete aside, let’s do the programming equivalent. Guess how many times I’m going to use the word “mock” or its derivatives in this course? I’ll count them up for you at the end.

03:38 See how well you can guess. There, I instantiated my fake object. Notice I didn’t say mock that time. I’m about to pass this on to our reader() function.

03:51 Take a second and predict what you think will happen.

03:58 Is that what you expected? So just what happened here. Note where the failure occurred. The call to .read_text() was fine. The Mock object created the method on the fly as I showed you before, but remember, when you call a Mock method, what you get is a new instance of a Mock object.

04:15 That new instance doesn’t support the use of len(), so the code there fails. If you really want to fake .read_text(), you probably want to return a string like it does, not a new Mock object.

04:28 Well, Mock lets you do that.

04:35 In case you skipped the previous lesson, everything in Python is an object, even the methods. You can associate attributes with a method. The difference here is there aren’t any parentheses, so the method isn’t getting called.

04:47 It’s being treated like data. You can hard-code a return value from a Mock method by setting the .return_value attribute on the fake method, which is what I’ve just done here.

05:00 The result is when the mock’s version of .read_text() gets called, it will now return the string faked, instead of a new Mock object.

05:08 Let’s try that out. And now because a string came back from a call, the rest of the reader() function works. By using the .return_value mechanism, you can fake out any method and hard-code the result.

05:22 If you were doing this inside of test code, you might also inspect the Mock object as well.

05:29 Checking how it got invoked, for example, or using one of the assert methods discussed in the previous lesson. The Mock object has several different ways of controlling what comes back from a method.

05:40 You saw the .return_value mechanism, but you can also cause method side effects. The .return_value mechanism returns the given value when the method gets called, but what if you wanted to raise an exception?

05:54 With the .side_effect attribute on a method, you can set an exception. Now when the method gets called, this exception gets raised. Note that .side_effect takes precedence over .return_value, so if they’re both set, like here, the .side_effect will be the one that fires.

06:13 Because .side_effect was set to ValueError, this time when .read_text() got invoked, the exception got raised. Side effects can be more than exceptions, though.

06:28 When you set a .side_effect to an iterable, each call to the method returns the next value in the iterable. Note how this is different from .return_value.

06:37 If you set .return_value to a list, you’d get back that list here. You’re going to get back the first item in the list the first time you call the method, the second item the second time, and so forth.

06:51 So 1, 2, 3,

07:01 and boom. The fourth call raised an exception because there was nothing left in the .side_effect to iterate over. .side_effect also has a third mechanism.

07:11 It allows you to define a quick function.

07:16 I’m using *args and **kwargs here so that anything that gets passed into the faked method can be passed into our .side_effect function.

07:25 My .side_effect function doesn’t do much. I’m just going to print out the calling args and then return a string.

07:42 Now that I’ve populated this .side_effect with a reference to fake_read_text(), I can now call the reader() function again. Our .side_effect function got invoked, since .read_text() doesn’t have any arguments, *args and **kwargs were empty, but you can see that our fake function got called because the result was side_effect_function as a string rather than the hard-coded .return_value attribute from before.

08:09 You can turn a .side_effect off by setting it to None.

08:18 Once I turned the .side_effect off, the .return_value attribute is working once more. So far, I’ve been calling a method on a Mock object, but you can also call the Mock object itself.

08:30 One advantage of doing it this way is you can configure the object in the constructor.

08:39 Here I’ve set both a .return_value and a .side_effect from the get-go. If I call the Mock object directly, I get the ValueError raised as a .side_effect.

08:50 If I None that out,

08:55 now invoking it results in the 3 .return_value attribute configured in the constructor.

09:03 You don’t really use Mock in the REPL or in standalone scripts. Usually, you’re using it inside of unit tests. In the next lesson, I’ll do a quick tangent on Python unit test scripts in case you’re not familiar.

09:15 Then in the lesson after that, I’ll add Mock back into the mix.

Become a Member to join the conversation.