Python for the .NET Developer Transcripts
Chapter: Testing
Lecture: Mocking our dependencies with pytest_mock

Login or purchase this course to watch this video and the rest of the course contents.
0:00 Well we made a good shot at it. We tried to write some test where we set the style as electric. And we call all guitars and we just do this
0:09 cool little assert statement to check that Hey we only got electric guitars back. That worked but as you recall
0:16 we were actually touching the two dependencies that we were not suppose to be touching. The Pytest-fixtures come to the rescue again here.
0:24 So what we're going to do is up at the top, is we're going to. Let me put it like this. import pytest-mock.
0:33 Remember that's one of the dependencies we added in addition to Pytest itself. So if we have pytest-mock we can come down here and we can say
0:41 that we have a mocker. This is another test fixture but instead of us defining it Pytest-mock does it. So let's just print out what this is.
0:52 And it's a little bit hard to see with the formatting. But we have a Pytest plugin mock fixture right there. And we can help us ourselves a lot
1:00 if we put some type annotations in this. So this is a list of guitar. And this one is a pytest-mock.mocker mock fixture. Here we go.
1:16 Those are the two we're using. And so far it says you're not using this but you know what we're about to. So we need to do two things here.
1:23 We need to mock out or patch in terms of Pythons terminology the two dependencies. So with .NET what you do is you actually pass interface objects in.
1:34 And instead of passing them real production ones you pass test one. Python is very easy to reach down and redefine what a function does.
1:42 And C# had some crazy post-compilation mechanisms to do this as well back in Team Foundation way back in the day. I don't think they
1:52 really push that much anymore. But the idea is we're going to reach down and basically forward this function
1:58 this test function here. We're going to redefine what a couple of the functions are. So here's how it goes. We say mocker.patch
2:08 Sigh. Here's *args, **kwargs. Again I hate this. While you're working it tells you nothing about what we have to do.
2:15 But what you have to do is you have to put the full name. So module-name.function-name. Now let's go down here and have a quick look.
2:25 Hit guitars from db. So this is the function that we're working with. We can say autospec. If anything here gets called its fine.
2:37 But what we actually want to specify is return value is this guitar data. That's our other test fixture where our test data's coming in.
2:47 We already saw that that's great. Let's see if this will run at all. Well that's a good sign. What's an even better sign is notice here. Our hey
2:57 we're going to the database. That should never happen. That's gone. Why's that gone? That's awesome! So let's go down here.
3:05 And when we call all_guitars a couple of things are happening. We're logging it that's the message that still is there.
3:11 And we haven't replaced that dependency. But this used to be a simulated database call. But now it's just a mock. And all it does is say hey
3:20 if you call me, you'll get that test data that we defined over in the other. No not that one. Our test fixtures. Here you get this test data
3:27 that we're returning from our test fixture. Okay so we call this one no longer going to the database as it were. Actually here. call this one
3:39 we're no longer going to the database. Because for the scope of this function Pytest-mock has redefined what that function means.
3:46 And it'll put it back when it's done. The other job we got to deal with here is we have to do this also. For lib.log
3:55 we don't care about any return value. We just need it to not get in the way. Let's run this again. Boom! There it is!
4:04 It's passed and you know, if we dig into this we go look at the output there's no output. Alright just to show you if I put this back and run it again.
4:12 There's our logging patch out again no more logging. Because we're no longer going to the database or going to the log.
4:20 We replace what those two dependencies mean. Now you might think oh we better put this into a try. Finally where we rollback the patch
4:27 or into a with statement Pythons equivalent of a using statement. Normally that would be true but the way this mocker works
4:36 is it's already implemented that. There's some clever stuff happening in the implementation
4:41 for the test fixtures. So just assume this is totally safe to do even if we have some kind of crash. Alright, cool, right!
4:48 So we've finally got this working. And now lets do one other test. Maybe it's a little silly lets just say test all_guitars.
4:55 And the style is going to be all like that. And we no longer can do this cool thing. Instead, we're going to have types. do a set comprehension.
5:09 So a little inline expression that generates a set. And this will just be g.style for g in guitars. Like that. And then our assert will be that
5:22 types is equal to the set with acoustic and electric. Let's run that and make sure everything is working. Perfect! It passes. Just to verify we're
5:36 actually testing something let's see what happens if we weren't quite right. Problem is we expected acoustic and we got what we're really looking for.
5:44 So perfect. It looks like this is testing what we're looking to test. The final thing is these two are good here. These are great
5:52 but we're only testing the happy path. In a moment we're going to test the not so happy path. To make sure that even when we pass bad data
6:01 maybe it doesn't succeed. But it fails in a predictable and expected way.


Talk Python's Mastodon Michael Kennedy's Mastodon