Programming Foundations: Writing, Testing, and Fixing Code

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

← Writing Pseudocode and Designing Step-by-Step SolutionsNext lesson: How Interpreters and Compilers Process Code →

Earn 3 college credits for Introduction to Artificial IntelligenceRecommended for credit by NCCRS · Computer Science 373 · self-paced · one payment, lifetime access

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.

Compare: AI vs Current Trends in CS and IT

Why take it here

Why students take Introduction to Artificial Intelligence with UPI Study

How machines search, reason and learnIntroduction to Artificial Intelligence covers how AI systems are built: intelligent agents, search and heuristics, game strategy, logic, neural networks, machine learning, language and perception, and the ethics of AI. It is a common upper-level course in computer science degrees.
Credit at a fraction of the priceThe same 3 credits can cost $750 or more at a US college. Here it is one payment, self-paced, with lifetime access.
Finish your degree soonerTransfer credits reduce the courses you still owe, so you can graduate earlier or lighten a heavy semester.
Strengthen an applicationA completed college-level course on an official transcript shows admissions and scholarship committees you can do the work.
Study on your own scheduleNo start dates, no deadlines. Watch the lessons, practice, and take the final when you are ready.
130,000+students helped with college creditsACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.

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.

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?
A syntax error stops a program from starting, a runtime error crashes it while it runs, and a logic error gives wrong answers without any message. A missing bracket is a syntax error, dividing by zero is a runtime error, and printing a total when you meant an average is a logic error. Logic errors are the hardest to find, which is why tests with expected results matter.
What makes a good set of unit test cases?
A good set mixes normal, edge, and invalid cases, each paired with an expected output decided before running the code. Normal cases check typical input, edge cases probe extremes such as an empty list or a single value, and invalid cases check input the function should not receive. Each test should check one thing, run independently of the others, and have a clear name.
What is a regression test in programming?
A regression test is a test kept in the suite after a bug is fixed so that the bug cannot quietly return. For example, after guarding a rescale function against an empty list and equal values, the tests that once failed stay in place. If someone reintroduces the bug later, that test fails at once and points to the problem.
Why should tests compare floating-point numbers with a tolerance?
Tests should compare floats with a tolerance because decimals are stored approximately, so a correct calculation may not print the exact value you expect. For example, (0.2 - 0.1) / (0.3 - 0.1) in Python does not print exactly 0.5. Checking that the result is within a tiny margin, for instance with math.isclose, avoids false failures caused by rounding rather than real bugs.

About the Introduction to Artificial Intelligence course

How many college credits is Introduction to Artificial Intelligence?
Introduction to Artificial Intelligence (Computer Science 373) carries a NCCRS credit recommendation for 3 semester credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
How much does Introduction to Artificial Intelligence cost and what is included?
$250, paid once, with lifetime access: all 69 video lessons, a practice quiz after each, the graded work and final exam, and an official transcript when you finish. Students who want several courses choose UPI Study PRO instead.
Which universities accept UPI Study credits?
UPI Study has signed partner agreements with universities such as Thomas Edison State University, University of the People and Charter Oak State College, and works with universities such as Penn State, SNHU and SUNY. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities. Use "Find my college" on this page to check yours.
How long does Introduction to Artificial Intelligence take?
It is self-paced with no deadlines. The 69 lessons total about 16 hours of video; most students add reading and practice time and finish in a few weeks to a few months, on their own schedule.
How are the exams done?
Quizzes and assignments are online in the course. The final exam is taken online with proctoring; a person on the UPI Study team checks assignments, usually within two days.
How do my credits reach my college?
When the course shows 100% complete, request the official transcript from your dashboard. It is sent to the college you name, and you can send it to more than one. Check your college first with "Find my college" on this page, or ask Athena.

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.

Introduction to Artificial Intelligence
3 credits · NCCRS
Enroll →