Developing and Testing Programs
Subprograms, readable and maintainable code, validation and authentication, the three kinds of error, and how to build a test plan that finds faults before an examiner does.
Subprograms
A subprogram is a named block of code that can be called from elsewhere. Python has one keyword for both kinds.
A procedure carries out a task and returns nothing:
def greet(name):
print("Hello", name)
greet("Amira")A function returns a value:
def area(width, height):
return width * height
size = area(4, 7)Parameters are the names in the definition, width and height. Arguments are the values passed in, 4 and 7. Parameters are what make a subprogram reusable: one that always calculates the area of a four by seven rectangle is useless.
Scope
A variable created inside a subprogram is local: it exists only while that subprogram runs, and it cannot be seen from outside.
def tally():
count = 0
count = count + 1
return countThis returns 1 every time it is called, because count is created fresh on each call. Local variables are the right default: two subprograms can each use a variable called i without interfering, and each subprogram can be tested on its own.
Why decomposition into subprograms matters
- Code written once is called wherever it is needed, so there is no repetition.
- A change is made in one place rather than in every copy.
- Each subprogram can be tested on its own before it is trusted.
- The main program reads as a list of named steps, which is far easier to follow.
- Define it once with def, naming the parameters it will be given.
- Call it with arguments. They fill the parameters, w = 3 and h = 4.
- return hands the result back to the line that called it.
- A procedure does a job. A function also gives a value back.
Readable and maintainable code
Edexcel awards marks for code quality, not only for code that works.
Meaningful identifiers. total_price rather than tp or x2. The next reader, including you in a month, should not have to trace the program to work out what a variable holds.
Comments that explain why. count = count + 1 does not need a comment saying it adds one. It may need one saying why the first record is skipped.
Consistent indentation and blank lines separating logical sections, so the structure of the program is visible on the page.
Constants for fixed values. VAT_RATE = 0.2 at the top, used by name, changed in one place.
Subprograms so each task has one home and one name.
Adding them at the end, under time pressure, produces the comments that restate the code and earn nothing. Written as you go they explain the decision you were actually making.
Validation, authentication and testing
Validation
Validation checks that entered data is reasonable. It cannot check it is true.
| Check | Tests | Example |
|---|---|---|
| Range | Value lies between limits | if 0 <= mark <= 100: |
| Presence | Something was entered | if name != "": |
| Length | Correct number of characters | if len(password) >= 8: |
| Type | Data is of the expected type | if entry.isdigit(): |
| Format | Matches a required pattern | if "@" in email: |
| Look up | Value is one of a known set | if county in COUNTIES: |
A validation loop is the standard pattern and is worth memorising:
mark = -1
while mark < 0 or mark > 100:
entry = input("Enter a mark between 0 and 100: ")
if entry.isdigit():
mark = int(entry)
else:
print("Please enter a whole number.")Authentication
Authentication checks the user is who they claim to be, usually with a username and password, strengthened by minimum length, mixed character types and a limit on failed attempts.
Validation asks "is that a sensible thing to type". Authentication asks "who are you". They are different jobs.
The three kinds of error
Syntax error. Breaks the rules of Python, such as a missing colon or bracket. The program will not run and the error is reported with a line number.
Logic error. The program runs and gives the wrong answer. Using + where - was meant, or < where <= was meant. Nothing reports it, which is why these are the expensive ones.
Runtime error. The program starts and then stops: dividing by zero, an index out of range, opening a file that does not exist.
Test data
| Type | Meaning | For a mark from 0 to 100 |
|---|---|---|
| Normal | Typical accepted data | 55 |
| Boundary | At the edge of what is allowed, on both sides | 0, 100, and -1, 101 |
| Erroneous | Should be rejected | "cat", blank, 3.5 |
Almost every off by one error appears at a boundary and nowhere else. A program tested only with 55 will happily reject 0 and accept 101.
Trace table trainer
Fill in a trace table row by row and have it marked as you type. This tool needs JavaScript, so turn it on to use it. Everything you need to answer the exam question is explained in the text above.
- Validation checks: presence, type, range, length, format and lookup.
- Validation checks that data is sensible. It cannot know whether it is true.
- Verification asks the user again, for example with a confirm password box, to catch typing slips.
- Three kinds of evidence: something you know, have or are.
- Two factors together are far stronger than one: a stolen password alone is not enough.
- Passwords should be stored as hashes, never as typed, so a leak does not reveal them.
- Test data for an age from 13 to 19, on a number line.
- Normal data is typical and should be accepted.
- Boundary data sits on and just outside each edge, because that is where mistakes hide.
- A test plan records expected against actual. A failed test shows what to fix.
Key terms you must be able to define
- Subprogram
- A named block of code that can be called from elsewhere in a program.
- Procedure
- A subprogram that carries out a task without returning a value.
- Function
- A subprogram that returns a value to the code that called it.
- Parameter
- A name in a subprogram definition that receives a value when it is called.
- Argument
- The actual value passed to a subprogram when it is called.
- Local variable
- A variable created inside a subprogram, existing only while it runs.
- Validation
- An automatic check that entered data is reasonable and of the expected form.
- Authentication
- Checking that a user is who they claim to be.
- Syntax error
- An error breaking the rules of the language, preventing the program from running.
- Logic error
- An error in which the program runs but produces the wrong result.
- Runtime error
- An error occurring while the program is running, causing it to stop.
- Boundary test data
- Data at the very edge of the acceptable range, on both sides of each limit.
Flashcards
Say the definition out loud before you flip the card. Retrieving it from memory is what builds recall, reading it again is not.
Reach the top grade on this topic
Paper 2 gives marks for a program that works and for a program that is readable, and the second is far easier to secure than most candidates realise.
Decompose before you type. Three named subprograms and a short main section will always score better than one long block, and it is quicker to debug.
Validate every input the task mentions. Use the while loop pattern, and say in a comment which check you are applying.
Choose test data with a reason. "0 and 100 because they are the boundaries, and -1 and 101 because an incorrect comparison operator would only show up there" is the answer a mark scheme wants.
Be exact about the three errors. Will not run at all is syntax. Runs and gives the wrong answer is logic. Starts and then stops is runtime.
Mistakes that cost marks here
- Saying validation checks that data is correct. It checks that data is reasonable.
- Giving only one boundary value. A boundary needs testing on both sides.
- Calling a crash a logic error. A program that stops while running has a runtime error.
- Writing comments that restate the code rather than explaining the decision.
- Initialising a counter inside the subprogram that is supposed to increase it, so it resets on every call.
The cost of fixing a fault rises sharply the later it is found. A mistake caught while writing a function costs minutes. The same mistake caught after release can cost thousands of times more, which is the whole reason testing exists.
Knowledge check
Exam-style questions
Five written questions in the style of the real paper. Write a full answer, then let the marker check it against the mark scheme.
- LCode written once can be called from many places, reducing repetition
- LOr each subprogram can be tested and maintained on its own
A task written once as a subprogram can be called from every point that needs it, which removes repeated code, and because that task lives in one named place a correction only has to be made once rather than in every copy.
- LUses a while loop that repeats until the value is acceptable
- LChecks that the entry is a whole number
- LChecks that the value is within the range 1 to 10
- LGives the user a message when the entry is rejected
A correct answer is: number = 0, then while number < 1 or number > 10:, then entry = input("Enter a number from 1 to 10: "), then if entry.isdigit(): number = int(entry), then if number < 1 or number > 10: print("That is outside the range."), else: print("Please enter a whole number."). The loop condition must be false only for acceptable values, the entry must be checked for being numeric before int() is applied so that typing a word does not crash the program, and a message tells the user what was wrong rather than simply asking again.
- LValidation checks that data entered is reasonable or of the expected form
- LAuthentication checks that a user is who they claim to be
- LGives an example of each, such as a range check and a password
Validation is a check on the data itself, confirming that what has been entered is reasonable and of the form expected, for example that a mark lies between 0 and 100 or that a required field is not blank. It says nothing at all about who typed it. Authentication is a check on the person, confirming that the user is who they claim to be, usually through a username and password or a biometric check. The two are separate jobs: a system with perfect validation still has no idea who is using it, and a system with strong authentication will still accept a nonsensical value if it does not validate.
- LA syntax error breaks the rules of the language
- LGives an example such as a missing colon or bracket, and notes the program will not run
- LA logic error means the program runs but gives the wrong result
- LGives an example such as using the wrong operator, and notes nothing reports it
- LA runtime error occurs while the program is executing and stops it
- LGives an example such as dividing by zero or an index out of range
A syntax error breaks the rules of the programming language, for example a missing colon at the end of an if statement or an unclosed bracket. Python cannot interpret the line, so the program does not run at all and the error is reported with a line number, which makes it the easiest kind to fix. A logic error is different in kind: the code is valid, so the program runs from start to finish, but the instructions do not do what the programmer intended, so the output is wrong. Using a plus where a minus was meant, or less than where less than or equal to was meant, produces a logic error, and nothing reports it, which means it can only be found by testing with data whose correct answer is already known or by tracing the program by hand. A runtime error occurs while the program is executing and causes it to stop, for example dividing by zero, reading past the end of a list, or trying to open a file that does not exist. The program was valid and started normally, so a runtime error usually reflects data the programmer did not anticipate rather than a mistake in the code's structure, which is why validation and error handling reduce them.