Basic Testing and Debugging

The course is part of this learning path

Start course
Overview
Difficulty
Intermediate
Duration
1h 51m
Students
13
Ratings
5/5
starstarstarstarstar
Description

In this course, we will discuss the topic of exceptions and debugging. We'll see that exceptions are objects representing exceptional situations and that these are usually problems in our code or in the environment in which our code is running. By using exception handling, we will know how to respond to problems when they arise.

Learning Objectives

  • Learn the proper syntax and techniques to work with exceptions
  • Understand inheritance in the context of Object-Oriented Programming

Intended Audience

  • Beginner coders, new to C++
  • Developers looking to upskill by adding C++ to their CV
  • College students and anyone studying C++

Prerequisites

To get the most out of this course, you should have a basic understanding of the fundamentals of C++.

 

Transcript

In the last lecture, we discussed how to create our own custom exceptions. Regardless of the types of exceptions we receive, or even if we don't receive any exceptions. It is very useful to be able to slowly move through our programs and to find where bugs exist. Often, our programs crash at a certain point and debugging tools can help us with this. But, even when our program doesn't crash, that is, when we have a design level logic error that doesn't cause an exception, such as code that results in incorrect output or a bad calculation; we can still use debugging tools to help us fix our code and make it more robust.

Some important terms must be discussed to give us a better understanding of testing and debugging. In simplified terms, a bug is a problem with our code. This could be a compile-time error, such as a syntax error that prevents our code from even compiling. A fatal runtime error which causes our program to crash when we run it, either immediately or when given some sort of input that it can't handle. And logic errors which we saw earlier were errors that are caused by a problem in the code's logic. Testing a program refers to trying to expose any bugs that may be present. Debugging refers to removing those bugs. You are literally debugging the software. Simple, right? Let's create a new project called BugFun. We will use this project to discuss and explore testing and debugging. So, we have Empty Project. We will call it BugFun. Good. And we of course, need a main file to start with. 'Add', 'New Item', main.cpp, we will add iostream, using namespace std, and we need to give main a body. Let's purposely leave out the semicolon and then return 0. So, see it, it's even confused. It doesn't know exactly what to do. So, we're purposely leaving out the semicolon. Now, let's run it or at least try to. So, we could just build it right now and then try running it. But it has to build before it runs anyway. So, start without debugging. And you notice it says; There were build errors. Would you like to continue from the last successful build? 'No'.

So, let's read down here what this says. So, this tells us that it expected a semicolon in the Error List, expected a semicolon. You can double click that, and it actually points to a line, and it says, syntax error: missing ';' before 'return'. So, even though the arrow right here is pointing to the return statement. You have to look around because a lot of times the error is not on the line it's pointing to but before it or somewhere around it. So, right here int a we know that, hey, that needs a semicolon. Sometimes, takes it a second to get the error to go away, but there was an error, and we couldn't even compile it. Okay, we fix it by adding the semicolon everything's hunky dory. Now let's add the exception class stdexcept and we need a function here int imBroken(int num1, int num2). And here's what we're going to do. We're going to have int imBroken(int num1, int num2)  and int result = 0. If num2 is not equal zero, then we set the result equal to num1 divided by num2. Otherwise, we throw an invalid argument and say "cannot divide by zero". Otherwise, we return result. So, it's a simple integer division so it will truncate any decimal component. Okay, so we need to fill in main here to get it to do anything. So, let's set a = imBroken (10, 5) and then print out a. So, not a good variable name, but it's simple and it's quick for right now for our purposes and now the one that will break or one that will break is this one right here because we would try to divide by zero. So, it's going to throw an exception. Let's run this and see what happens; start without debugging. It just says, something's wrong. So, that prints out the two and why does it do that? Because we don't have a try catch here. Right. So, let's try it with the debugger and see what happens. 'Debug', 'start debugging' and then you'll notice it gives us a little more information. So, we have an exception. Now you and I could probably figure out what the issue is here. We can't divide by zero. When we try, we set up our imBroken function to throw an invalid argument exception, so it does. But we are catching it in main where we call the imBroken function, so it crashes. For the sake of using the debugger, let's see if it can tell us more information than what it's telling us currently. So, whenever you want to freeze the execution of your program at a certain point while it's executing instructions, you can use a debugging feature called breakpoints.

Let's put a breakpoint next to the line where we declare the result variable. So, right here I'm going to actually you want to make sure when you close this you got to stop debugging. I'm going to put a breakpoint by clicking on the margin over here. So, note that if you're using a different IDE like Xcode or Code::Blocks, you're not using Visual Studio or anything else. That some of the steps may vary but you should still have very similar concepts and commands like breakpoints and the step controls, which we will see shortly. So, watch carefully how I set that breakpoint. You just left click in the margin and when you click it again it goes away. So, you're basically toggling this breakpoint over here. Let's run the application with the debugger and see what happens. So, 'debug', 'start debugging', and you'll notice it has stopped where we put the breakpoint.

Excellent. So, you should be able to see this arrow now pointing that will be the next instruction that it will execute. Now, one important note is that it's the again, next statement to be executed. In other words, that line has not been executed yet. So, if we hover over num1, it says 10 and num2 says 5, that was from our first example right here. But if we hover over result, it's some wackadoodle number because that's what's left in the memory at the location that result exists and it has not been set to zero yet. Once this line is executed, result will equal zero. So, that's pretty cool. So, we see the values of these parameters from the call that was made in main specifically we see the values again from the first call. Now, if you look near the bottom of the screen here, I'm going to bring it up here a little bit first. You'll see that there is call stack over here which could be useful and there's also autos, local and watch down near the very bottom. If I change the tab so that it shows the watch 1, it will allow me to add things to the watch list. So, I'm going to place variables in the watch list. We could do it this way, I could say num1 and you'll notice it says what the value is and the data type. If I put num2 it will do the same thing and even result tells me it's garbage data right here. If I wanted to remove result, I could right click and say 'delete watch'. If I want to add it back, here's another way to do it, right click, and then you can even tell it to 'add watch'. So, it goes right there. Pretty cool. So, we see the values in all of those variables that gives us some information. Since we're still in debugging mode will progress the instructions by looking under the debug menu first. So, let's see what we can do.

So you should note that there is a step into, step over, and step out option. Note that they have shortcut key combinations too it will be different if you're on Mac or something else. But we have F11, F10, Shift+F11 for those three. If you hit continue, it takes you to whatever the next breakpoint is. So, in this case we have one breakpoint but since the function called twice, it'll actually go and continue until we go to the next call. All right, so these require a little bit of explanation. We have them on the command bar as well and this will be the one I usually prefer. You'll notice that we have the run, show next statement, you have step into, step over, and step out. Again, let's explain these though. Step into means if your current execution line is a function call that when you click this, it will go into the function. Step over treats function calls like a single statement, and they don't take you to these functions but instead execute them as if they were a single line and just move on to the next line. Step out means if you're currently inside of a function that was called, and you want to go back to the calling function then you click the 'Step out'. Let's go instruction by instruction in our code and see what happens. So, let's go by hitting 'Step Over'. So, I'm on this line right here. I'll 'Step Over' it. As soon as I step over it, you should see in the Watch List and also if I hover over the variable, it says the result = 0.

The num1, num2 have not changed. We have this num2 is going to be compared to the literal 0; their result is 0 here. So, we can keep going by hitting 'Step Over'. You'll notice it went in here because this num2 != 0; 5 is not equal to 0. So, it gives us or goes to the line where result, which is currently 0, will be set to 10 divided by 5. And that will give us 2. So, I 'Step Over'; it goes there. Result is now 2 both on the Watch List and also when we hover over it. And if I hit 'Step Over' this time, look what happens: it goes straight down to the return because it skips over the else. So, basically, we've slowed down our programs which usually go very fast to something a human can handle, namely us. It doesn't matter how smart you are, you are not going to be able to process things as fast as a computer; it's just impossible. So, unless you invent a neural interface in the future but I don't think we're there yet.

So, we go here and we've got the result is 2, and that will return back to the calling function if I hit 'Step Over' and 'Step Over' again. Now it goes here. We are on this line. a has not been set yet. But as soon as I hit 'Step Over', it goes to the next line and look at here,  a is now 2 because that was the return value. It will print out the 2. If we bring up the console right here, you can see the 2 was printed out. I'm going to minimize it. Now we're on this second one here. Now if I hit 'Step Over', it's going to, well, steps over it. However, you could go back in it if you hit step in. So, 'Step Into', right there; goes back in the imBroken, we have 'Step Over', 'Step Over'. Now look here carefully 'Step Over', notice that it skipped this because 0 is equal to 0; it goes to the else, and it's just about ready to throw this invalid_argument. So, let's hit 'Step Over' and we get the exception. So, it gives us quite a bit of information. So, we want to stop debugging and remove the breakpoint and then fix our error. So, we can probably, even if you didn't know before, you probably figure out that it's an invalid argument exception that we threw, and that's exactly what happened. So, we can fix that by taking the appropriate action in main. So, around this code right here, we put a try. Let me set it up, and then we'll move the code. catch(const invalid_argument & err). And now I'm going to cut and paste or drag. In this case, I can drag the code right here into the try block, so that it could be tested. Now inside of the catch, let's print out << err.what() << endl.

And, of course, we'll run it again. This time, we could stop debugging of course, and then you want to make sure that you start without debugging or you could use debugging; it doesn't really matter. Here's our console, you'll see that says 2, and then it says Cannot divide by 0. So, instead of crashing, we are now catching it and then printing out what this says err.what() Cannot divide by 0. Where did that statement come from? Well, it's stored in the message that means it had to have been passed to a constructor. Whose constructor? invalid_argument's constructor. So, we are now catching the exception, and a lot of bugs are much more complex than this one, where we essentially planted a bug on purpose in this example, so that we could explore the debugger. As a very brief challenge, set a breakpoint at the beginning of main and run the debugger. Only step into the second imBroken call, and look at the variables. Step over as you go along. So, pause the video, run the debugger, come back if you're having trouble or when you're done checking out the debugger.

How'd that go for you? Were you able to properly use the debugger? Let's do it together. So, we'll place a breakpoint at the very top of main, right here. A lot of times we want to put it at the first actual executed statement. So, we can use the Step Over, but we're going to Step Into the second imBroken call. So, let's see what happens. Debug, start debugging. It moves down here because this didn't really do anything. So, we're on the try, and we're going to say 'Step Over' and the first imBroken call, we're going to 'Step Over'. We're going to 'Step Over' the print, and now that we're on the line that's going to call the imBroken again. But it's the second call; we want to step into it. So, I'm going to click 'Step Into'. Now I'm here and we were just supposed to kind of look at the variable values and see what happens in here. So, again, looking again, result, num2, num1, num0. We pretty much did this before, but I did a little faster and I wanted you to go back and have to repeat the process but to do it on your own. Sometimes repeating something on your own is a good way to learn things. And also we didn't take as much care looking at the different variables. We've learned the fundamentals and the importance of testing and how to use Visual Studio's debugger in this lecture. As I mentioned before, if you're using a different IDE, it should have similar step-controls and debugging mechanisms built-in. In the next lecture, you'll be reinforcing your skills with a project to test your ability to work with built-in exceptions. Let's get going.

 

About the Author
Students
308
Courses
20
Learning Paths
4

John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.

Covered Topics