We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Managing Mocks

00:00

Formal Metadata

Title
Managing Mocks
Title of Series
Part Number
69
Number of Parts
169
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Helen Sherwood-Taylor - Managing Mocks Mocking is a valuable technique for writing tests but mocking effectively is often a stumbling block for many developers and can raise questions about its overall value as a technique. There will be a brief introduction to mocking, then a look at features and techniques of Python’s unittest.mock library and cover some useful tips and common scenarios, so this will be useful to those who have some experience mocking but would like to do so more effectively. ----- Mocking is a valuable technique for writing tests but mocking effectively is often a stumbling block for many developers and can raise questions about its overall value as a technique. The audience will have some familiarity with unit testing and may have tried mocking before, but some introduction will be provided for those who haven’t. We will look at some features and techniques of Python’s unittest.mock library and cover some useful tips and common scenarios, so this will be useful to those who have some experience mocking but would like to do so more effectively. Summary of proposed content: 1. A short introduction to what mocking is and why it is useful. 2. Tour of Python’s mock library and how to make the most of it * Creating and manipulating Mock objects * Setting up return values and side effects to control test environment * Inspecting mocks - different ways to examine a mock object and find out what happened during the test * How and where to patch 3. Common mocking situations - scenarios where mocking is particularly useful and/or tricky to get right. For example - date/time, filesystem, read only properties 4. Some discussion of when mocking is and isn't helpful. Focus will be mainly on Python's unittest.mock module but we will also have a brief look at some other useful libraries.
11
52
79
Software testingExecution unitObject (grammar)Sound effectDivisorServer (computing)Software testingMultiplication signProjective planeInteractive televisionDatabaseGoodness of fitCASE <Informatik>File systemWritingUnit testingProper mapLibrary (computing)outputIntegrated development environmentSoftwareSystem callRevision controlInstallation artObject (grammar)Data structureCodeSound effectException handlingElectronic mailing listSocial classCategory of beingRoundness (object)Programmer (hardware)Expert systemGame controllerEndliche ModelltheorieMathematicsRight angleCodeSoftware frameworkPhysical systemFunctional (mathematics)Group actionDifferent (Kate Ryan album)Semiconductor memoryOperator (mathematics)Set (mathematics)Lecture/Conference
Revision controlSystem callPairwise comparisonObject (grammar)Parameter (computer programming)Electronic mailing listSoftware testingOrder (biology)Social classSystem callMultiplication signTest-driven developmentAttribute grammarGoodness of fitCodeFlagRevision controlPairwise comparisonSound effect2 (number)Exception handlingMessage passingIterationInstance (computer science)Cycle (graph theory)InformationObject (grammar)Green's functionForcing (mathematics)CircleFrequencyExistential quantificationCategory of beingRight angleEvent horizonComplex (psychology)Flow separationEndliche ModelltheorieCASE <Informatik>Ocean currentBit3 (number)Traffic reportingGame controllerStandard deviationLecture/Conference
Expected valueOptical character recognitionObject (grammar)Control flowPairwise comparisonHecke operatorPatch (Unix)Functional (mathematics)String (computer science)Water vaporPoint (geometry)MultiplicationModule (mathematics)Patch (Unix)Game controllerMatching (graph theory)SoftwarePhysical systemMereologySoftware testingObject (grammar)Social classCodeUtility softwareNamespaceLevel (video gaming)System callMultiplication signExpected valueNumberParameter (computer programming)QuicksortRaw image formatData managementContext awarenessRevision controlLine (geometry)Integrated development environmentOrder (biology)Correspondence (mathematics)ChainCASE <Informatik>Observational study1 (number)Sampling (statistics)Greatest elementProtein foldingMobile WebPattern languageGroup actionDistribution (mathematics)Declarative programmingLecture/ConferenceComputer animation
Patch (Unix)Object (grammar)Patch (Unix)Software testingObject (grammar)Context awarenessMultiplication signComputer animation
Installable File SystemCategory of beingOpen setCodeComputer fileFile systemFile formatMultiplication signInterface (computing)Utility softwareChainPatch (Unix)Module (mathematics)Parameter (computer programming)Line (geometry)Library (computing)Context awarenessRight angleQuicksortSocial classCategory of beingBitObject (grammar)Axiom of choiceMatching (graph theory)Set (mathematics)Error messageIterationCodierung <Programmierung>FreezingPhysical systemEndliche ModelltheorieLocal ringData modelMathematicsComputer animation
Patch (Unix)Social classCategory of beingLoop (music)Functional (mathematics)DatabaseMultiplication signResultantParameter (computer programming)BitSoftware testingPatch (Unix)Social classCategory of beingObject (grammar)Dot productSuite (music)Computer simulationError messageModellbasiertes TestenSound effectMereologyPhysical systemType theoryComputer animation
Library (computing)Software testingMultiplication signSlide ruleReading (process)GodExecution unitUnit testingQuicksortTwitterRepository (publishing)LaptopInteractive televisionBitComputer animation
Lecture/Conference
Transcript: English(auto-generated)
We're gonna get started with Helen and managing mocks. Please give her a big round of applause. Hi everybody. My name's Helen. I'm a freelance programmer. I've been using Python for quite a few years. This is my first EuroPython.
I'm really excited to be speaking here and I'm really enjoying Bilbao and having a great week. So I decided to write this talk. I originally wrote it for PyCon UK last year and I did that not because I felt like I was an expert on mocking but because it was something that I'd tried
working with quite a lot and I'd struggled with and felt like I was hacking my way around and not really taking the time to understand it properly. Then I started a new project and it had lots of API interactions and databases that I didn't own and things that were quite a good case for mocking.
And so I wanted to start doing things nicely and write lots of tests and try and understand this properly. So it's something that I've struggled with and learned a lot about and I wanted to share
a few of the things I've learned. So what is a mock? A mock is a fake object that replaces a real object and the main use for this is in unit testing. Mocks are a way to control and isolate the environment in which your test runs.
So why would you want this? Well, first of all, you want your tests to be deterministic. If they are failing, it should be because there's something wrong with your code, not because of some external factor like a server being down. You need to be able to trust your tests. If you don't trust them, you'll start ignoring them.
Mocking gives you more control over the inputs to your test. You can simulate different environments for your code and make sure it responds correctly to all kinds of scenarios. It's also fast. It's an in-memory operation, so it's generally quite a lot faster
than the things it's replacing like network calls and file systems. And the speed of your tests is really important. And again, if they're too slow, you'll stop running them. So I'm mostly going to be talking about the mock library that comes with Python. This is called UnitTest.mock.
Don't worry about the unit test name. You can use it with any framework. It works quite well with pytest and other things. So it used to be a standalone library. It was written by Michael Ford and it was brought into Python in 3.3. If you're using earlier versions, you can still use the standalone version
and that's actively maintained and kept up to date. So Python 3.3 plus, just do UnitTest.mock. Otherwise, pip install mock and then import mock. So we're going to start with a look at mock objects. You'll find yourself working with these quite a lot.
So you import mock with a capital M and that's the mock class. You set that up with whatever values you want it to have. So here I'm setting a value and then I can get that back as a property of the mock. If you try to use a property that you haven't set,
that's another mock. And if you try to use one of its properties, that's also another mock. So it's mocks all the way down and you can kind of dig down and set the values of these mocks and get them back. Mocks are also callable.
And when you call a mock, it returns another mock. So we've got more mocks. If you want to change what your mock returns, then you can set its return value and then when you call it, and you'll get back the value that you set.
So you can dig down as far as you want into this structure and you can set, just go as deep as you want and this is all about setting up the right, environment for your code to pick up on. Another way that you can set up what your mock does when it's called is side effects.
So a side effect can be an exception. So if you assign an exception class to side effect on a mock, then when that mock is called, you'll get that exception raised. It can also be a function, so you can override the behavior of that.
I don't find I use this a lot, but it is there if you need it. And a side effect can also be a list of things you want to happen. Now this is useful if you want, if your code is going to be making multiple calls to this mock and you want a different thing
to happen each time. So I can set up side effect with a value, an exception, and then another value. So the first time I call it, I'll get the value. Second time, it will raise the exception. Third time, I get another value. And if I try to call it again, I'll get a stop iteration
because I ran out of side effects. And if you want it to go on forever, it's just an iterable, so you can have cycle or something. So while you're doing things to mocks, they are recording everything that's happening to them. They record all the calls that are made
and all the arguments that are passed in. And you can get that information, you can query that information using these assertion calls. And most of them want you to say which arguments you expected them to be called with.
So we've got cert called with, which looks at the last call that was made. It doesn't care what happened before that. You've got a cert called once with, which is the same, but it makes sure that it was only called once. You've got a cert any call, which just looks at whether that call was ever made with those arguments, doesn't care about the others. You've got a cert has calls,
which you can give it a list of calls and you can specify whether you care about the order. Then you've got a cert not called, which just, you check that it wasn't called. You need to be a little bit careful with that last one because, so if you do this in Python 3.5,
you create a mock, you call it, and then you assert that it wasn't called, you get an assertion error. And that's kind of what we might expect to happen. You do the same thing in Python 3.4. You create the mock, you call it, you assert not called.
And all that happens is that you get back another mock. And if that's in a test, it will just run and it won't fail. Now, the reason for that is that assert not called didn't exist before Python 3.5. So when we try to use it, it just behaves like anything
that we might try to access on the mock. So yeah, that just passes and that's maybe a little bit dangerous. If you do plan to use that call, make sure you know which versions of Python people are gonna be developing with. And yeah, maybe just don't if you can't.
So that's a little bit dangerous and it has caught me out a couple of times. Probably because I wasn't doing red-green TDD properly, which is bad. Python 3.5 thankfully does something about this.
So we get these safety checks. If you try to call something beginning with assert or a sweat because you might make typos, it will complain. So we've got assert call, which is being added in Python 3.6.
We try to use that in Python 3.5, we get an attributes error. And that's kind of good because it's telling us that something's wrong with our code. So, and if you really have things that begin with assert and you want to use them, then you can, there's a flag called unsafe you can use to switch that off.
Now, when you're looking at, when you're using these assertion calls, you generally have to say which arguments you want to be passed in. You're expecting to be passed in and they're generally fairly fussy about that. But sometimes you might not care
about what one of the arguments was. Maybe your argument is particularly complex. So you're testing them all in separate tests. So you can use mock.any for that. And you can just pass that in, pass it in as in place of an argument.
And that just says, I don't care what the value of this argument is. If you want even more control there, you can use comparison objects. So these aren't really a special mock thing. They're just a nice Python thing with magic methods,
which you might've been to other talks about this week. If you have an object, an instance of a class that implements the EQ magic method, then you can implement a custom comparison and pass that in as a check in your assertion checks.
So for example, here, I'm checking whether my function was called with a multiple of five, it was, and then with a multiple of four, which it wasn't. And I've got a nice, nicely formatted string to describe what went wrong as well.
You can have even more control over the inspection of your calls. Maybe you want to work at a lower level. You can say, was this mock called? How many times was it called? And get access, sort of raw access to all the arguments that it's called with.
So we've looked at a lot of features of mock objects. Let's have a look at how they fit into your tests. As a general kind of pattern,
we'll be creating a mock and making sure it's in the right place. Then we'll be setting up the values on it, the environment for your test. Then we run the code under test, and then we check that our expectations have been met. So first of all, let's look at how we get a mock into place.
Now for this, we use patch. So this tells Python where to put a mock. You give it a path to a module or a class or a function or anything that can be looked up as a path, and it will set things up
so that when that path is looked up, it will give you back, rather than giving you back the real thing, it will give you back a mock. And you get access to that mock in your test. So you can manipulate that mock and kind of get that injected into your code via patch.
Now the object it actually gives you is a magic mock, which is like a mock object, but it has some of the magic methods set up for convenience. We don't need to worry too much about that. So there are a few different ways you can access patch, depending on what your situation is.
First of all, we have a decorator. So I've just got a little example function which I'm going to be testing. It uses requests to contact the GitHub API. It fetches a JSON document for a user and extracts that user's number of followers
and returns it. So it just looks like that when we run it. So we patch our test method using request.get. We get a mock object passed into our test method as a parameter.
We can then manipulate the values of the mock. So we chain all the way down through return values and objects and more return values and set up what we're going to pretend is being returned from this API. And then we can run our assertion and check that the right value came back.
You can also move it up to the top of your test class if you've got one and have it as a class decorator. If all your test methods are patching the same thing, then that's quite handy. You can stack multiple patches, but you need to be careful about the order in which you do that.
It kind of works from the inside out. So the bottom-most patch corresponds to the first parameter and so on. Patch can also be a context manager. So we get a mock into our context and that survives, the patch is active for the lifetime of the context.
So this gives you more control over the lifetime of your mock, if that's what you need. If you have any kind of clashes with other stuff that does clever things with parameters like pytest or other such things, that can be quite handy there.
I tend to use the decorator version when I can because I don't like long lines of code and these make your code longer. Okay, so you can have even more control
by calling start and stop on the object returned by patch. I don't find that to use too much, but it can be useful. One thing I particularly have struggled with in mocking was patch paths and how the lookups work
and what path to use when I'm patching. I often found that mocks weren't appearing where I was expecting them to and I was getting very confused. So I just want to look at how this works. So I've got my get-forwards function again
and it's in a module called GitHub utils. And I've got a test for that. So the test imports GitHub utils. GitHub utils imports requests and pulls it into its own namespace. Coming back to our test, we patch requests.get.
We get our mock. We manipulate it and then we call get-followers. get-followers looks up requests.get. Because we patch that path, it will get back the mock. So our test behaves as we might expect it to and it passes.
If you consider a slightly different example where I've done a from request to import get at the module level rather than looking it up inside the function, the test is almost exactly the same apart from the import. So if I import GitHub utils to,
that imports get-from-requests. Then our test patches requests.get. It gets to the mock, manipulates it, calls get-followers. get-followers called get. Get's already been looked up on requests and it was looked up before we patched.
So what's gonna happen is we're gonna get a slight delay and then we get something that doesn't match the value we set up. And that's because it was talking to the network because we patched. You might say we patched too soon.
A better answer to that is that we patched the wrong thing and this is how we fix that. We patch the thing that our module has already looked up. So we patch it on the module itself because it owns a thing called get.
So our test looks like this and that works. The Python docs say ensure you patch the name used by the system under test. Another useful thing is patch.object.
You can use this to attach a mock to another part of an object that you're already testing. For example, here I've got a simple greet function on my user class that says happy birthday to the user if it's their birthday. I'm testing that function and I want to mock his birthday. So I get a user object,
I patch it by passing in the object itself, the name of the method I want to patch and the return value I want it to have. And when I get inside that context and the user.isBirthday is a magic mock that will return true.
So that will make my test work. Mocking the time can be quite tricky. The date module is written in C so you can't mock individual bits of it. If you try, you'll probably get something like this. You can mock the whole thing
but it does mean you've got to kind of chain all the way down to the bit you want and you don't get a choice at whether you're mocking the other bits. So using the, it's my birth, another kind of birthday-based method. So I patch that, I modify the return value of today and that works.
A possibly nicer way of doing that is the freeze-gun library, which is pretty cool. You pip install freeze-gun and it comes with a decorator called freeze-time and you can, that takes all sorts of interesting human-friendly formats that you can put in. You can have dates and date times and so on.
And that's, it's pretty straightforward to use and I quite like that library. Mocking the file system is also quite tricky. There's a utility method called mockOpen which helps you out here. You give it, you give it a parameter called readData which you can tell it what data
you want to pretend your file has and it will set up a special mock that has the right, all the right sort of file, file handle stuff. And when we, because we're using open, which is a built-in, we need to do something slightly different when we patch that and we used,
we need to use create equals true because we can't overwrite the built-in. We need to kind of create a local copy of open that our code is going to pick up on. But then once, yeah, once we've got that, we can open and read our file. There's one little problem with this. You can't iterate over that file handle.
Your file handling code probably has a nice Pythonic interface like this. If you want that, then you've got to modify the mock that you get back from mockOpen and chain all the way down to the iter magic method and set up the lines that you want. Note that if you're doing that in the context manager,
you need to go through the enter magic method as well. If you want to mock a property, so we've got our person class again, I've made his birthday a property now.
If you try to patch the object that doesn't work, you get this error. Not entirely sure why, I mean, I guess it's something to do with the way that properties work. Yeah, you need special handling for that. And for that, there's a parameter called newCallable
which lets you say what type of mock you want to create. And that's a special mock class called property mock set up just for this purpose. And the other thing there is you need to patch the class rather than the object, which might be a little bit limiting, but as far as I know, that's the only way to do that.
So I've just got a little example of some, of some mock-based tests. It's based, I've got this very crude retry function.
You pass it another function and it tries over and over to call it until it succeeds. If you get a database error, it will wait a little bit and then try again and it will keep doubling that delay. Okay, so when we're thinking about testing this, I can see two big reasons for mocking.
First of all, we've got this time dot sleep, which if it's in our tests, then it's gonna slow our test suite down. And secondly, we want to be able to simulate database errors without, we don't want to cause real database errors.
So this is a test that I've written for this function. So we patch time dot sleep and that gets rid of the delays for us. We get a mock object passed in as our parameter. Then we create another mock inside the function and we set it, this is going to be the function
that's gonna be run each time. And we set it up with three side effects, two failures followed by a success. We call our retry function and then we have a look at what happened. So first we check that it was called three times because we had two failures and then a success.
Then mock sleep assert has calls, we're just checking that, we're checking the calls, the delays that were made and making sure that it was doubled each time. And then we check that the result that came back was what we set up as a return value for that function.
So that's just a little example for you. I'm nearly out of time so I had some little bit of stuff about when you mock and why you should mock. I'm gonna post these slides online afterwards and also got some recommended reading for you.
The Testing Goat book by Harry Percival is very good and talks quite a lot about why you should mock and the advantages of mock and how it can help drive better design. Gary Bernhard Fast Test Slow Test is a very good talk about the same sort of things, it's worth watching.
And the Unit Test Not Mock Library, I couldn't possibly cover every feature of mock in this time that I've tried, but it's a great read.
So I'm Helen ST on Twitter and GitHub and there's also a GitHub repository of this Python notebooks with this material. I know the slides are quite dense so there's some interactive examples in there. Thank you. Thanks very much. Please thank the speaker with, yeah.