Exceptions6:53 with Craig Dennis
It is a great idea to verify that exceptions are thrown in your code when you intend them to. They are part of your happy path. Let's go catch some!
- Fast. Like super fast, or they won't be run.
- Isolated. Make sure you are only testing what you intend to test.
- Repeatable. Every test run should return the same results. Be careful of time and intermittent failures. Over-specifying expectations are usually a sign of possible breakage.
- Self-verifying. If tests are passing, you should feel safe that things are good to go to production. If they are failing, you should fix them, and trust that something indeed is broken. Trust trust trust.
- Timely. Write tests either before or as you write code. Don't fall behind!
We're starting to really learn how to right good tests. 0:00 Before diving in how to handle verifying that exceptions are happening like we want 0:03 them to, let's take a quick look at another testing mnemonic device. 0:07 The first rule to writing a good test is this. 0:11 Tests should be fast. 0:15 If they don't run fast, the more likely it is the developers won't run them. 0:17 Unit tests will run fast because they should be isolated. 0:22 You should not be relying on other code. 0:26 You should use test doubles to make sure that you are only running the code that 0:28 you are testing. 0:31 We'll talk more on this in a bit. 0:33 You always wanna make sure that your test is repeatable. 0:35 You should get the same results every time you run the test. 0:38 You wanna make sure to avoid intermittent failures and over specify. 0:42 Your test should be self verifying. 0:47 When your entire test suite passes you should feel comfortable 0:49 that you can ship your code to production. 0:52 When any test is failing, you should ensure you fix it because something is for 0:55 sure wrong. 1:00 If you can't trust your test and 1:01 the results, their usefulness starts to diminish. 1:02 Take time to make sure that you and 1:06 your team can verify that the system is working. 1:07 Write your tests in a timely manner. 1:10 If you are not using the development practice of test-driven development, 1:13 you should still make sure that you write tests when you are developing a feature. 1:16 Tests help document what your code is expected to do and 1:21 verifies that it is working the way that you, the developer intended. 1:24 Check the teacher's notes for more on each of these concepts. 1:29 So we have already experienced how to handle exceptions in our code. 1:32 We can either throw them or catch them or use a try catch block. 1:36 Now you might be tempted to add tries and caches to your test. 1:40 I mean it's Java code, it would work. 1:43 In fact, this is an older way of doing things, you would try some code and 1:46 expect it to fail and in the cache you'd assert that it happened. 1:49 It's a lot of boiler plate and annotations really help expose 1:53 a better way of asserting that exceptions are happening. 1:56 Let's go use them. 1:59 One more quick thing to talk about before we get started here. 2:01 Did you notice how the tests are all stating that they throw exception? 2:03 This is a best practice. 2:09 And what this allows for is that you don't need to try and 2:10 catch things that throw specific exceptions. 2:13 Now the logic here is this, if an exception happens and 2:16 you didn't intend it to, let the test runner report it. 2:20 It will bubble up and out of the test method and 2:23 the runner will report the exception. 2:25 So over in AlphaNumericChooser you see how this locationFromInput throws 2:27 InvalidLocationException. 2:32 Now notice how our test over 2:35 doesn't complain that we didn't catch the exception and 2:37 that's because it has this throws exception clause at the end, right? 2:40 So let's remove that temporarily. 2:43 Now it's complaining, right? 2:47 And if I look at the intention action here, 2:48 it's gonna say add the exception to the methods signature. 2:51 Now I wanna show you this because this is a pitfall, right. 2:53 So if you do that. 2:56 It's gonna say the specific exception that you would want, but we don't want that. 2:58 The best practice is to use the base exception, right? 3:02 And invalid location extends that. 3:06 So it is still saying that it will throw that, 3:08 which is why it's allowing it to pass. 3:10 Cool. 3:12 All right, so let's make a new test and have it actually throw an exception. 3:13 In fact, let's check and 3:17 see that the happy path that throws the exception works as we intend it to. 3:18 Okay, so what is that though? 3:23 So let's look back here. 3:25 Okay, so if it doesn't have the right buttons, right, if we put 3:27 the wrong input in there and it doesn't match, it's gonna throw one of those. 3:32 So let's do that. 3:35 Let's go back to our test and we'll make a new test here. 3:36 And let's call it choosingWrongInputIsNotAllowed. 3:41 Okay. 3:50 And then, we don't really care about the value that's returned, right? 3:51 So we use that chooser object, and 3:55 we'll do location from input, let's just write wrong in there. 3:57 Just make it kinda clear that, that's happening. 4:01 And what that's gonna do, that's gonna throw an exception. 4:02 So in fact, why don't we run it and see what happens? 4:04 Okay, boom. So it blew up with a red exclamation. 4:09 Before it was yellow saying that it was a fail, 4:13 this is saying that there's a big thing. 4:14 So it says it threw this invalid button. 4:15 So it's throwing that message. 4:18 Okay, so the test annotation actually has a field that we can set, 4:20 named expected, and it takes a class. 4:26 So let's do that. 4:28 So we are gonna say that this test here, expected. 4:29 And it expects the InvalidLocationException class. 4:36 Cool, and now if we run it, boom. 4:41 It passes. 4:44 And now if we cuz it to actually not blow up, it [INAUDIBLE] expected exception. 4:45 Like let's give it an actual good input. 4:51 We'll make it A1 and let's run it. 4:52 And boom. Now it fails and 4:56 it says that we expected the exception and valid location. 4:57 Exception. 5:00 Pretty cool, right? 5:01 So let's put that back to wrong. 5:02 So, what do you say we catch the other happy path exception in that method? 5:06 Remember we saw it in AbstractChooser. 5:11 Let's go back to that real quick. 5:15 In AbstractChooser here, if you try to create a location that's 5:16 larger than the max rows and the max columns, it will throw an exception. 5:22 It will throw another invalid location exception. 5:27 Why don't you pause me and give it a swing? 5:30 When you finish un-pause me and I'll show you how I did it. 5:32 Ready? Go catch that exception. 5:35 Okay. 5:38 So let me show you what I did. 5:39 Command in. 5:45 Test method, and we're gonna say choosingLargerThanMaxIsNotAllowed. 5:46 Okay, and same sorta thing, 5:56 we're gonna say that it's expected, 6:00 invalid location exception.class. 6:05 And we're gonna choose one that's way bigger, right? 6:15 So let's say locationFromInput, 6:19 we'll give it a good B, but let's make the second one on the row big, so like a B52. 6:21 Love shack, baby. 6:26 And blammo. 6:29 Test pass. 6:32 Hm, there was something in the creditor class that we didn't look at. 6:33 Let's go back over there to that creditor class. 6:38 There was something in here. 6:40 Oh yeah, there's not enough money. 6:41 It throws a not enough fund exception. 6:45 I see a couple of happy paths there that we didn't cover in our tests yet. 6:48 Wanna go ahead and take that on? 6:51
You need to sign up for Treehouse in order to download course files.Sign up