UPI Study › Courses › Introduction to Artificial Intelligence › Free lessons › Lesson 3.2 Free with a free account
Programming Foundations: Writing, Testing, and Fixing Code
Introduction to Artificial Intelligence · Chapter 3: Programming Foundations and SecuritySection 3.2 · lesson 19 of 6913 min · free with a free account
Free preview
Create a free account to keep watching
It takes a minute and costs nothing. Your free account unlocks all of Chapter 1 and 3 more lessons of Introduction to Artificial Intelligence.
Lesson complete
Earn the 3 college credits for Introduction to Artificial Intelligence
Enrolling adds a quiz after each lesson, the graded work and the official transcript. Recommended for credit by NCCRS.
Next lesson: How Interpreters and Compilers Process Code →← Writing Pseudocode and Designing Step-by-Step SolutionsNext lesson: How Interpreters and Compilers Process Code →
In this lesson
Write small Python programs, test them, and debug them systematically.
- Use variables, control flow, functions, and data structures to write small programs
- Design unit tests with normal, edge, and invalid cases
- Distinguish syntax, runtime, and logic errors
- Apply a step-by-step debugging cycle using print tracing or a debugger
Free study notes
Get this lesson's notes by email
The key points and 4 practice questions with answers, in your inbox. One email, free.
One email. Unsubscribe in one click.
Transcript
Everything said in the video, section by section (1,948 words). Read along, or search it.
Opening Problem
An Average That Printed 7,250
A student writes a short program to average 100 exam scores, each marked between 0 and 100. It runs without a single complaint, and prints 7,250. Nothing crashed. Yet no average of scores between 0 and 100 can be 7,250. It's the total, not the average. Writing code is only the first part of programming. Checking that it does what you meant, and finding the cause when it doesn't, take just as much skill.
Lesson Roadmap
Four Ideas Behind Every Language
This lesson covers all three skills: writing small programs, testing them, and fixing them. We'll write everything in Python, but the ideas apply to any language. Every language is built from the same four ideas: variables, control flow, functions, and data structures.
Definition
Variables Hold Typed Values
A variable is a named place in memory that holds a value. You choose the name, store a value in it, and can change it later. Each value has a type, which decides what you can do with it. Here, three is an integer, a whole number. 19.99 is a decimal, which Python calls a float. Spam, in quotes, is text: a string. And True is a Boolean: true or false, like is this prediction correct? Run it, and Python prints the total, then the four type names. And choose names that explain themselves: pass mark tells a reader far more than x.
Core Concepts
Sequence, Selection, Iteration, and Functions
Control flow is the order in which a program carries out its steps. Three building blocks cover almost everything: sequence, selection, and iteration. Load the data, then clean it, then count it: that's sequence. If the spam score is high, label it spam: selection. For each email in the inbox, compute a score: iteration. A function is a named, reusable block of steps. It has a name, parameters for its inputs, a body that does the work, and a return value that it hands back. Functions avoid repeated code, let you read a program as meaningful steps, and, as testing will show, a small function with clear inputs and outputs is easy to check.
Data Structures
Lists, Dictionaries, Sets, and Records
A data structure organizes many values together, and choosing the right one makes a program simpler and faster. A list holds items in order, reached by position, starting at zero. A dictionary looks values up by key, like how many times each word appears. A set holds only distinct items, and answers one question: is this item present? And a record groups the fields that describe one thing, like one patient's measurements. Run it: position zero of the list is 88, the count for win is 3, spam is in the set, and the patient's age is 54.
Planning Method
Five Steps Before You Type
Real AI systems are built from many small pieces of code that count, clean, transform, and compare data. Plan each piece before typing, in five steps. State the task: what goes in, and what should come out. Then work a tiny example by hand. Win, free, win, should give win two, free one. Break it into steps: start empty, and for each word, add one to its count. Write the code, with the structures that fit: a dictionary and a loop. And check it against your hand-worked answer.
Worked Example
Counting Words With a Dictionary
Here's the word counter. Counting how often each word appears is the first step in many text tasks, like spotting spam. A dictionary suits it: each word is a key, and its count is the value. If the word is already there, add one. Otherwise, start it at one. Run it on win, free, win, now, win, and it returns win three, free one, now one. One small function combines a variable, a loop, a selection, and a dictionary.
Worked Example
Rescaling Values From Zero to One
A second program. Models often work better when numbers share a common scale, so this function maps the smallest value to zero, the largest to one, and everything else in between. For each value, subtract the low, and divide by the range, high minus low. On 10, 20 and 30, it returns zero, a half, and one, as intended. Keep an eye on this function. It has a hidden weakness, and testing is about to expose it.
Worked Example
Spam Threshold and Accuracy
A third: a simple spam rule and its accuracy. predict turns a spam score into a decision, using a threshold. accuracy counts how many predictions match the true labels, and divides by the number of labels. With scores of 0.9, 0.2, 0.7 and 0.4, and a threshold of a half, the predictions are spam, not spam, spam, not spam. Three of the four match the true labels, so the accuracy is 0.75: the same accuracy measure from the lesson on data quality.
Definition
Test Cases and Unit Tests
How do we know a function works? Running it once and glancing at the result isn't enough. Testing means checking code against results you decided in advance. A test case is one input, paired with the output you expect. A unit test is an automated check, written as code, that runs one small piece on a test case, and reports pass or fail. Because unit tests are code, they rerun in seconds after every change, so any new mistake shows up at once. Good cases come in kinds. Normal cases check typical input. Boundary, or edge, cases probe the extremes and odd corners. And invalid cases check how the code reacts to input it shouldn't get.
Tests in Action
Seven Test Cases Expose Three Failures
Here are seven test cases for rescale, each with its expected output written first. For the edge cases, we decide the right behavior: equal values all map to zero. The cases cover normal values, negative numbers, values already in range, all equal, a single value, an empty list, and order kept. Run the tests. Four pass. All values equal fails with a division by zero, and so does a single value. The empty list fails too: there's no smallest value. Three failures, that a quick check with 10, 20 and 30 would never reveal.
The Fix
Two Guards and a Regression Test
When every value is the same, high minus low is zero, and the division fails. An empty list has no smallest value at all. Two guards fix both. If the list is empty, return an empty list. If high equals low, return a list of zeros, one for each value. Rerun the same tests, and all seven pass. And they stay in the suite. If someone reintroduces the bug next year, a test fails at once. A test kept for that purpose is a regression test.
Best Practices
Habits of Good Unit Tests
Good unit tests share a few habits. Test one thing at a time, so a failure points to one cause. Keep each test independent, so they can run in any order. Give tests clear names, so a failing report reads like a description of the problem. And decide the expected result first, or you may copy whatever the code produced, bug included. Compare decimals with a tolerance, because decimals are stored approximately. Ask Python for, 0.2 minus 0.1, over, 0.3 minus 0.1, and it doesn't print exactly a half. And remember: testing shows bugs, never their absence. A passing test only proves that that case works. So choose cases that could expose mistakes.
Quick Check
Spotting the Edge Case
Quick check. A new function returns the middle value of a list of numbers. Three test inputs are proposed. Which one is the edge case? Pause, and decide. The list with just five. A single value sits at an extreme: it's both the smallest and the largest, so it probes a corner the code might mishandle. Three, one, two is a normal case. And a list of letters is the tempting one, but it's an invalid case: input the function shouldn't receive at all.
Debugging Basics
Syntax, Runtime, and Logic Errors
A bug is a mistake in a program, and debugging is finding and fixing it. First, know which kind of error you're facing. A syntax error appears right away. The program refuses to start, and points to the broken line, like this missing closing bracket. A runtime error appears while the program runs: it stops with a message, like dividing by zero, or reading past the end of a list. And a logic error is never announced. The program runs happily, but gives wrong results. The opening story was one: the average that was really a total. Tests exist largely to catch these.
Debugging Process
Reproduce, Isolate, Understand, Fix, Verify
Three types, then. Syntax errors stop the program from starting. Runtime errors crash it. And logic errors give wrong answers silently, which makes them the hardest. A reliable process beats guessing. Reproduce the bug, isolate where it happens, understand why, fix it, and verify the fix. Change only one thing at a time, so you know what made the difference. And explain the problem out loud, even to a toy on your desk. Putting it into words often reveals the flaw.
Reading Errors
Reading a Traceback From the Bottom Up
An error message is a free clue. Call rescale's first version on three equal values, and read what Python reports, from the bottom up. The last line is the kind of error: ZeroDivisionError, a runtime error. Above it is the location: inside rescale, at line 6, the division. And above that, the call chain: how the program got there. Line 3 of demo.py called rescale.
Worked Example
Tracing a Boundary Error With Print
Now a logic error. count passing should count the scores that reach the pass mark. A unit test expects 40, 50 and 60, with a mark of 50, to give 2. It gives 1. Trace it, with a print statement that shows each score, the test result, and the count so far. 40: false, correct. 50: false, but 50 reaches the mark. That's the mismatch. 60: true. The condition uses greater than, where it needed greater than or equal. A classic boundary error.
The Fix
Greater Than or Equal Solves It
Fix it: change greater than, to greater than or equal. Rerun, and the trace shows 50 counted, and the result is 2. That's the whole cycle. Reproduce: the test fails every time. Isolate: the score of exactly 50. Understand: the mark itself should pass. Fix the comparison. Verify, and keep a new boundary test.
Tools
Breakpoints, Stepping, and the Call Stack
Print statements are simple, and work anywhere. But they clutter the code, must be removed afterward, and need a rerun for every new question. A debugger lets you pause a running program and look inside, without editing it. Mark a line with a breakpoint, run, and it stops there. Then step over a line, treating a call as one step. Step into a function you suspect. Step out to its caller. Continue to the next breakpoint. Watch shows variables' values, and the call stack lists the chain of functions that led here. It takes a little time to learn, and repays it quickly.
Quick Check
An IndexError Past the End
Quick check. This function should return the last score in a list. It's called on 88, 92, and 79. What happens when it runs? Pause, and predict. It stops with an IndexError: a runtime error. A list of three has positions zero, one, and two. len of scores is 3, so it reads position 3, past the end. It isn't a syntax error: the line is perfectly well-formed, so the program starts. And 79 lives at position 2. Writing len of scores, minus one, fixes it.
Key Takeaways
Write, Test, and Debug in Cycles
To sum up. Every language is built from variables, control flow, functions, and data structures. Plan a program first: define what goes in and out, and work an example by hand. Unit tests check one function against results decided in advance: normal, edge, and invalid cases. Keep them, as regression tests. Syntax errors stop a program starting, runtime errors crash it, and logic errors give wrong answers silently. Debug in a cycle, not by guessing.
Reflection
Designing Tests for a Tip Calculator
Something to think about. Pick a small everyday task, like calculating a tip. Describe the function you'd write: its inputs, its output, and three test cases, one of them an edge case. And can a program pass every one of its tests, and still contain a bug? How?
About Introduction to Artificial Intelligence at UPI Study
Introduction to Artificial Intelligence (Computer Science 373) is a self-paced online course of 69 video lessons in 9 chapters, from intelligent agents and search to game strategy, neural networks, logic and reasoning, machine learning, language, robotics and AI ethics. It carries a NCCRS credit recommendation for 3 semester credits.
Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Enroll now → Course details Download the syllabus All Computer Science courses Every course with PRO →
Compare: AI vs Current Trends in CS and IT
Why take it here
Why students take Introduction to Artificial Intelligence with UPI Study
Where the credit goes
Universities that work with NCCRS
Introduction to Artificial Intelligence carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Thomas Edison State UniversitySigned partner
University of the PeopleSigned partner
Charter Oak State CollegeSigned partner
Strayer UniversitySigned partner
Columbia Southern UniversitySigned partner
Indiana TechSigned partner
Penn StateWorks with NCCRS
Southern New Hampshire UniversityWorks with NCCRS
SUNYWorks with NCCRS
+many more cooperating universitiesFind my college →Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.
FAQ
Questions students ask us
About Programming Foundations: Writing, Testing, and Fixing Code
What is the difference between a syntax error, a runtime error, and a logic error?
What makes a good set of unit test cases?
What is a regression test in programming?
Why should tests compare floating-point numbers with a tolerance?
About the Introduction to Artificial Intelligence course
How many college credits is Introduction to Artificial Intelligence?
How much does Introduction to Artificial Intelligence cost and what is included?
Which universities accept UPI Study credits?
How long does Introduction to Artificial Intelligence take?
How are the exams done?
How do my credits reach my college?
Ready for the full course? Earn 3 credits in Introduction to Artificial Intelligence.
69 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.
3 credits · NCCRSEnroll →