pytest-mock : Mocking in pytest

The pytest-mock plugin provides a fixture called mocker. This fixture acts like a thin wrapper around the patching API provided by the mock package, which is now more commonly known as unittest.mock. But hold on, what is mocking? What is mock? Why is the pytest plugin wrapping a unit test submodule? And what extras does this plugin give us over straight unit test mock? But even what's the difference between a mock a stub and a spy and a fake and do i even need to care we'll get into all of that in this episode by the way pytest mock is sitting at number three on the top pytest plugin list let's get a plan of attack to cover all of what we need to cover today so let's cover what is mocking patching and monkey patching what if any is the difference between a mock, a fake, a spy, and a stub. Why might we need these for testing? And then we'll get into some history of mock in Python and how mock became unit test.mock. Then we'll take a quick look at unit test.mock directly and specifically patch.object, patch.object with autospec and using these as context managers.

There's a lot more in the mock package that we're not g oing to cover in this

episode, but we're going to cover enough so that we can jump into pytest-mock. The pytest-mock plugin provides a fixture called mocker. One of the cool things it does is it cleans up after itself. There's also some convenience functions. So we're going to talk about mocker.patch, mocker.spy, and mocker.stub. And finally, is it really okay to use unittest.mock from pytest?

Well, the answer is yes, but it seems weird until we look at all the history. Why do we mock and what do all these words mean there's a lot of new words here if you're new to this also the entire concept might seem wacky when we're testing software there are times when it helps to replace a part of the system with something else for instance if Let's say we've got some part of the system that reaches out to an external system or has some side effect that we don't want to happen during testing.

Like maybe sending an email, charging your credit card, or hitting a rate limited or slow API. We need to make sure that that stuff works also, but for the test in question, maybe we don't care. If we really don't care, we can put a stub in place, an object or a function that can mimic the thing in question but doesn't do anything. So just mimic, it just like is in place so our code can call it, but it doesn't do anything. That would be like a stub. But we might want something like a stub, but also we want to make sure that the code calling this thing is calling it correctly. That would be a spy. So we could like reach in, look, ask the spy, hey, did you get called the right way? Or maybe we want the action to happen, but we can't really test the outcome. So we still want to make sure the thing is called right and we want it to actually do the thing, but we want to find out how it was called. We also might want a fake thing that kind of responds as if the action happened, like returning data, but it doesn't actually do anything. So maybe it returns pre-canned responses or something.

Those are all kind of lumped into the mox, buy, stub, all of that stuff. And there's a lot of different definitions and they sort of overlap. The term patch also is sometimes we talk about like the thing that we're replacing in there as a patch, but also the term patch is used to describe how we're updating the system. So we patch a part of the system with a fake or a spy or a stub, or we patch it with a patch.

There's also monkey patching it's really the same thing as patching but sometimes it's simpler for instance with the pi test pi test has a built-in fixture called monkeypatch that doesn't do all the stuff that unit test mock does but it does a lot so all of these things all these concepts are related and that's why i'm really grateful for to Michael Foord when he originally implemented the mock library that he didn't distinguish between all of these he just called all of these things mocks so mock objects in unit test.mock are all of these they're spies they're fakes they're stubs you can do all of that stuff with unit test mock objects so let's get into the mock library the mock library had some life on its own before it became unit test.mock as part of the python standard library in python 3.3 the library is actually quite large and it does a lot but when When I use unit test.mock directly, I normally just use patch.object with or without autospec. And I use that with a with block. And that returns, so it's a context manager. I use it as a context manager usually. And it returns a mock object that you can make it, you can set it up to behave just as you want as a spy or return. You can modify what a function is going to return or whatever.

And then after the with block, the whole thing is unmarked. So whatever you patched, it gets cleaned up after the with block, which is awesome. What about this term auto spec? An auto spec to object or a function is just an object or a function that you, when you create it, you give it the thing you want patch and patch object to copy. And it copies the exact interface of the thing that you're patching. So the specification is automatic, thus auto spec. That's a little confusing, to me at least. But if I only want to mock a couple things or one or two things, I'll often pull in just unit test.mock with patch

object used as a context manager. But now let's turn to pytest-mock because it's kind of awesome.

pytest-mock claims to be a thin wrapper around unit test.mock. And it is, but it's also more, and I like it. You see it's simpler to use partly because the interface is restricted well it's not really restricted but it seems restricted and that's nice the way you use it is you include a mocker fixture in your test param list just like you use any other fixture and then with that mocker fixture you can call patch patch object spy or stub patch and patch object act just like mock.patch and mock.patch object. So usually I'm using patch.object. Don't try to use them as context managers. That's not supported by pytest-mock. What you do instead is you just, once you patch something, it gets cleaned up

automatically at the end of the test function. Because mocker is a fixture, it can have a teardown and it cleans up. That's so cool. What about these other things called spies and stubs? Well, in the pytest-mock nomenclature, Sure.

A mocker, if you call mocker.spy, you get a patch object that returns something that behaves just like the original. So it does, it has the full behavior, but that you can ask it questions about how it was called.

So in mocker, in pytest-mock terminology, mocker.spy means do the thing, but also I'm going to ask you about it. And then mocker stub is the other one. So marker.stub returns something that does not behave like the original, but you can still ask it how it was called. So that would be more like just a normal mock object. And then each of these, all of these, like patch object, marker.spy, and marker.stub, you can pass in autospec equals true to those two. And it works just like unit test.mock. The autospec will copy the specification of the thing that you're mocking. That's because it really is a thin wrapper around unit test.mock and the things it returns are objects from the unit test.mock library and you still have access to the full power of unit test.mock however this limited interface into how to use mocking is very refreshing and i think that is pretty much what i always need i need to patch an object and or i might need to use an object as is but spy on it, or I might want to stub the whole thing and not have the normal behavior.

So those are really the things that I would want from mocking normally. So I really like that the plugin focuses on that.

I did mention this earlier, but I want to mention it again. There is monkeypatch.

pytest has a built-in fixture called monkeypatch. It might do what you need it to do when you're looking to mock something. So check that out first. Check out the interface to Monkeypatch and what you can do. It might be enough. You might not need to reach for unitest.mock or pytest-mock.

Probably should do an episode on that directly, even though it's not a plugin. I'm going to put links to all this in the show notes. We're going to have a link to the top pytest plugin list, the pytest-mock documentation, unitest mock documentation. And then i did an entire episode with Michael Foord on the podcast a while ago i'm going to put a link to that but i also think i might like re-release that so that you can even if you don't want to go back in the back history you might have it just show up in your feed because it's a really great episode to talk about mocking and then reference to monkeypatch so you can look up what pytest monkeypatch is like and that's all for today thanks.

Creators and Guests

Brian Okken
Host
Brian Okken
Software Engineer, also on Python Bytes and Python People podcasts
pytest-mock : Mocking in pytest
Broadcast by