Building data-driven web apps with Pyramid and SQLAlchemy Transcripts
Chapter: Testing web apps
Lecture: Testing without the database
0:01 Things looked so good.
0:02 We were about to test our simple little register view model
0:07 but even just that, just testing the view model
0:09 turned out to unearth a serious, serious problem.
0:14 Let's run it again. I can see it's failing.
0:16 The failing is None type is not callable.
0:20 Well once you look into it you realize
0:22 the DBSession.factory is not initialized
0:25 because we've not set up the database.
0:28 Wait, we don't want to set up the database.
0:29 Why is this using the database?
0:32 This one we might be able to get away without the database.
0:35 Let's try. This one we're going to do it with no email.
0:40 We make sure that there's an error and we can also assert
0:43 that email is in that error.
0:48 Okay so we're expecting some kind of error message
0:50 to be set like email is required.
0:51 Right? So let's run that.
0:55 We don't need to call that out too explicitly there.
0:59 Run it again, how are we doing?
1:05 No sadly this one is also running into that problem.
1:09 If we structure it like this, if we just make that an L if
1:12 which would be reasonable
1:14 so the first time we hit an error we bail.
1:18 We probably could get one of these to pass, let's see.
1:24 Yes, our no email came back and it did find that
1:28 it was an error set and the word email appeared in that error.
1:33 But this one, this valid one, is never
1:36 going to work without the database, okay.
1:38 We'll leave this one here.
1:40 This one can go without the database but this one
1:42 we're going to need a new technique.
1:45 I have to introduce you to a new idea here
1:47 under this Act section.
1:49 What we want to do is we want to go to the database
1:51 more importantly we want to go to this data service
1:54 and this is why it's really awesome that we
1:56 wrote this service separately
1:58 is we're calling this function find_user_by_email.
2:00 How do I know?
2:02 Here, oh now it says you must specify your name, okay.
2:05 So let's go and do that really quick.
2:08 We'll get an error I was going to show you back.
2:09 This will be name. There we go.
2:18 Now we get our None type error back.
2:20 We go look, you can see it's calling find_user_by_email.
2:24 Well the problem is it should be going to the database
2:28 to check that user.
2:29 What we can do is we can use something called mocking.
2:32 Mocking will change the behavior of this user
2:36 service module and this function in particular
2:38 so let's go do that. We're going to use a context manager
2:41 so we only redefine it really narrowly within the context.
2:45 So say with unittest.mock.patch.
2:48 Oh we need to import that.
2:53 patch and then we're going to give it the target.
2:56 The target is the full string name that we're looking for
2:59 so pypi.services.user_service.find_user_by_email.
3:08 We're going to set the return value to be None, okay.
3:13 I'm going to define that with block.
3:15 Let's make this a little more readable.
3:20 And only in that little block when we call
3:23 this one function does it not actually call that
3:25 find_user_by_email, but it just returns none
3:28 for whatever values are passed in there.
3:31 Okay now so this should work because
3:34 it's going to come back, validate is going to call
3:37 this function, this function is going to return None
3:39 to say hey there actually is no user
3:43 by that email address you thought
3:46 so it should be fine. Let's try it again.
3:49 How awesome is that? Oh my goodness.
3:52 Okay so really really cool.
3:54 I'm going to expand it out and you can see
3:56 it ran pretty quickly. Let's do one more test here.
3:59 Let me duplicate this and do one other test.
4:02 register_validation_is_valid, I'll say existing user.
4:07 So let's test what happens if we try to register
4:11 as an existing user and let's say that this user does exist.
4:16 Say we're going to return a user
4:18 which we have to import locally.
4:22 I still kind of like these up at the top
4:24 in our arranged bit.
4:25 I'm going to create this user and it doesn't really matter
4:27 what we set here to the test.
4:29 It's just going to say we called that, a user came back
4:30 so there must be an existing user with that email address.
4:33 Now we're going to assert.
4:36 This is not None and existing is in the error.
4:42 Let's give it a shot.
4:43 So this way, we're controlling
4:44 a different outcome for validate.
4:46 One more time. Boom look at that.
4:50 Expanded out, existing user.
4:53 All right, you want to see what the error is.
4:55 We could print out error just to see it real quick here.
4:59 User already exists. Oh no we can't continue.
5:03 So that's how we're able to punch out these dependencies.
5:06 We'll go in here and just for this little Act section
5:11 we are using a context block along with the patch
5:16 aspect of unit testing to change the behavior of
5:22 We might have to do this for more than one method
5:24 and that would be fine, but here we are.
5:27 We're going to change this behavior this time to
5:30 return a new user so we can check for this validation case.
5:33 Previously to say no there's no user with that email address
5:36 whatever you gave me, so validation should pass.
5:39 Pretty awesome.
5:40 We could use this for email systems or databases or
5:44 calling web services, calling purchase gateways.
5:46 You name it, this allows us to get in the way
5:51 of all these other dependencies that we have.
5:54 We saw that Pyramid itself supplies some
5:57 testing primitives that we can use
6:00 like our fake request that we can pass
6:02 to get our register view model up and running.
6:04 That's how we test these view models.
6:06 You can also see that's how we
6:07 test the view methods as well.