Building Data-Driven Web Apps with Pyramid and SQLAlchemy Transcripts
Chapter: Testing web apps
Lecture: Getting started with tests
0:00 And see we have a new project pypi_testing. Again, this just sort of a save point. Let's open that up. Now when we created this, we used Cookiecutter.
0:11 We said cookiecutter pyramid starter. And it asked a few questions, and boom. Out popped a bunch of stuff.
0:17 One of the things we got, was this little test section. Now you can see, it's creating some view tests based on the built-in unit tests framework.
0:29 We could also use pytest. But it's using the built-in one. So we are going to use the built-in one as well just to be consistent.
0:35 But it really doesn't matter which framework you're using. And it's just doing standard things here. It's setting up and tearing down a few things.
0:42 And then it's testing one of the views. I noticed it's creating this testing.dummy_request. Where does that come from?
0:52 Pyramid. So pyramid has built-in testing capabilities to do things like give us either a fake request or a fake web app that it sort of spins up
1:02 out of its internals. And then here we're calling some what used to be a view method passing the request getting the model back from it.
1:10 Then we can test things like hey this model that comes back, does it have a project? I think this is maybe project details something to that effect.
1:19 Anyway this used to be a valid test. So here's how we're going to test. Some of it here you can see a more advanced version.
1:27 These are the whole web app. We're creating not just a test, a dummy request but a test web app based on our main method that we're calling
1:35 here from our __init__. So these are some of the things you're going to work with. But instead of just working with these tests here
1:43 we're going to go and do something better. We're going to organize this into things that are focused around our controllers and grouped in that way.
1:52 Let's go over here and say new package, I guess. And we will call this tests. And we'll leave this one here for a moment. Eventually we'll delete it.
2:02 But for now, lets just leave it as an example. And let's focus initially on the account. So remember to create different test files
2:11 for the various controllers, I will call it account_test. And we're going to get started by importing unittest and creating a testcase.
2:25 And the way this works in this framework is everything derives from testcase. And then it has methods that are test something.
2:34 All of these test_ whatever methods will be run by the test framework. Okay. So let's suppose we want to test register
2:46 validation, valid. I want to do valid one. I want to do another one that'll say no email let's say. Let's do this first one here.
2:58 Now I'm going to arrange these tests structure them in a way that I think is a pretty good pneumonic for folks.
3:06 It's going to use what's called the 3 A's. The 3 A's of testing. Which is Arrange, Act, then Assert. So we're going to do all of our setup.
3:18 Then we're going to do the one thing that we're trying to do, in this case validate our data. And then we're going to assert that it's valid
3:25 or something to that affect. Depending on whatever outcome we're after. So what's the arrange? Well let's arrange things by creating
3:33 one of these register view models. So it's going to be a register view model. Now, we could import this like so
3:43 where it goes up at the top. That's fine. However, look back over here. There's this, or the convention that they're using
3:52 where they're importing the dependencies only within the test. And the benefit there is that this only runs if we run the test.
4:00 It doesn't add any overhead. Where would the overhead come from? Well, if we look over here when we call config scan to go find all the routes
4:08 it's going to look through every file including the tests. And we want to isolate them as much as possible.
4:14 So we can do that by taking a step back here and say import locally, like that. So let's go over here and call this out as a range.
4:22 Great. Now we have to pass something. A request. Oh, where do we get that from? Well we sort of saw that before.
4:29 So we're going to go import pyramid.testing. And we'll get a dummy request. There's also sorts of things we can get.
4:40 Dummy session. Dummy rendering factory. We want a dummy request. And we're going to pass that request here. Now one thing that's a little annoying
4:49 remember on all of our view models we said this is a request. There's not a good enough base class that's shared across dummy request
4:58 and real request that describes all the things that happen there to make make the intellisense and whatnot useful. So this is going to complain.
5:06 And we can just say, no no this is going to be fine. Just don't check this. All right, so this arranges everything. Now we have to act vm.validate.
5:15 So validate the data. And then we have to assert. So self.assert is None. vm.error. So we want to say, there are not is not, is None. There we go.
5:31 We want to say there is no problem with this. And the way it's working right now it's not going to be so great. We're not done arranging.
5:40 We haven't passed any data. But let's just run this. See our test fail. It says that, you know, here's a string
5:46 that is, you know, is invalid in some sense. So let's try that. How do we run our test? Well, we can come over here and we can right click
5:55 and we can say run unit test in account. Let's do that. We're going to say run. We get a nice little test runner over here. This one that did nothing
6:03 oh, of course it passed. But this one, it failed with some errors. Let's see. It said, hm, in fact it just crashed
6:11 trying to do this. Look at that error. DBSession.factory execute that line right there. find_user_by_email is crashing because that is None.
6:25 And this really highlights one of the challenges that we're going to run into. We're trying to do this register thing and we want to validate it.
6:32 But this is actually going to the database. All right, but let's finish our arrange first. So in order to simulate submitting the form
6:40 we have to go to the request. And we got to go to Post. And we have to say this is going to be having an email, which is empty
6:48 and a password, wait, this is valid, right. Let's put some valid email there. The letter a, that's my favorite. All right so here's an email.
6:59 Here's a password. This should work. Let's run the test again. But we're getting this database thing. All right, whew, that's annoying.
7:08 We're going to come back and we're going to figure out how to fix that. But before we do, let's just do one more bit of overall organization.
7:15 If we split our test into multiple places here like we're going to have PackageController tests a lot of package tests we're going to have tests here
7:29 and we're going to have tests there. And we don't want to keep right clicking that it run these, no run those.
7:32 We want an ability to just say run them all. So let me show you really quick a technique we can use for that. Another file, _all_tests.
7:40 And what we can do is we can import all of the other tests. So we can import let's just do * and package_test.
7:53 We can PyCharm no no, let's just leave these alone. These are meant to be here. I know they look like they do nothing.
7:58 But they do something. And we can go run this. It's not going to come out as good as you think. So notice it doesn't even have
8:05 the ability to run unit tests. 'Cause PyCharm doesn't see it. So here's a super simple little hack we can do.
8:14 So we can just create this little, effectively empty test class. So PyCharm when we click over here says oh, running the test there.
8:23 And then it actually runs all tests which always always passes. But, then we see these others over here like that.
8:32 Yeah. So we'll be able to organize them from this. And that way we just add stuff here and just keep running the same command.
8:38 And we won't skip or accidentally overlook any of them. All right, with that in place we're ready to come back and work on this next.