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

Avoiding Weird Bugs via Specifications

00:00 In the previous lesson I showed you how to use patch() when dealing with dates and times. In this lesson, I’ll cover how to configure your mock objects more specifically.

00:09 By default, any method you call on a mock object is valid and returns a new mock object. This can be a bit of a problem if you’ve made a typo; an incorrectly spelled call will also work.

00:22 Your tests might not behave the way you want because you’ve accidentally set .return_value on a method that isn’t called because you fat-fingered the name.

00:30 You can get around this problem by explicitly telling Mock just what is and is not valid. You do that by providing a specification. Let’s head back to the REPL to configure some mock objects.

00:43 I’m going to revisit my example that used a Path object to read the messages text file

00:57 and that’s how it’s supposed to work. Now let’s mock it out.

01:06 Say I was faking out this path. I would call .read_text() on it,

01:14 which of course returns a new instance of a mock object. So far this should be so much déjà vu, but did you catch that? I forgot the E. Had I done the same thing on the real object, I would’ve got an error.

01:29 Since I’m using a recent version of Python, I even get a “Did you mean” message, and yes, yes, I did mean. When you construct a mock object, you can configure it to avoid this problem.

01:42 The spec attribute takes a list of names of methods which are allowed.

01:51 .read_text() works, but at this time, if I forget the E, I now get an error. It even triggers the same “Did you mean” mechanism. Instead of giving it a list of method names, you can also give an object.

02:08 By providing Path as a spec, I get back a mock object with the same methods that Path has.

02:17 So .read_text() works, but without the E I get an error. I like this way better than providing a list of strings as a specification because you could always put a typo in those strings as well, whereas this way guarantees it’s going to be right.

02:35 If instead of mocking an object, you’re mocking a module, there is a helper function that makes your life easier. It’s called create_autospec(), and it works similar to passing an object in as a spec.

02:49 First I’m going to need a module. Then I call create_autospec(), passing the module in,

03:01 and the result is a mock object representing the module, which in this case includes a MagicMock Path object.

03:14 Like with the spec on an object, if I try to invoke something that isn’t there, I get an error, as it wasn’t part of the autospec.

03:25 You can do something similar with arguments to patch(). I’m importing a new module to fake out. Then inside of the context manager, I specify that I’m mocking out random.

03:41 Remember, __main__ is the execution module for the REPL. So I’m telling patch() to mock out the random module that I just imported into this REPL.

03:51 By setting the autospec=True argument, I’m telling patch() to spec out the fake using the original as its pattern to mimic, which is pretty clever.

04:10 So inside the context block, the call to .randint() returned a MagicMock since it’s a real function and that got included because of the autospec.

04:19 But then the call to .not_a_thing() which isn’t a thing in the random module caused an exception, as you would expect. I highly recommend that you use the specification feature of Mock when you’re testing.

04:31 It could save you from having to track down some weird bugs inside of your test code. The last lesson is next. In it I will summarize the course and point you at some further resources.

Become a Member to join the conversation.