0% found this document useful (0 votes)
2 views18 pages

W3 - Good Programming Practices - Course Notes

The course notes for ENG1014 cover good programming practices, emphasizing the importance of writing reusable code, using functions to modularize code, and documenting and testing code effectively. Key topics include avoiding hardcoding, creating user-defined functions, and understanding variable scope. The notes provide guidelines for implementing these practices to enhance code maintainability and adaptability.

Uploaded by

p7sjtjkdvd
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views18 pages

W3 - Good Programming Practices - Course Notes

The course notes for ENG1014 cover good programming practices, emphasizing the importance of writing reusable code, using functions to modularize code, and documenting and testing code effectively. Key topics include avoiding hardcoding, creating user-defined functions, and understanding variable scope. The notes provide guidelines for implementing these practices to enhance code maintainability and adaptability.

Uploaded by

p7sjtjkdvd
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

ENG1014 Engineering Numerical Analysis Course Notes

ENG1014 Course notes


Week 3. Good Programming Practices
WEEK 3. GOOD PROGRAMMING PRACTICES ....................................................................................... 1
3.1. HARDCODING ......................................................................................................................................... 2
3.2. HARDCODING ......................................................................................................................................... 3
3.3. LAMBDA FUNCTIONS................................................................................................................................. 8
3.4. MODULES AND PACKAGES ......................................................................................................................... 9
3.5. DOCUMENTATION .................................................................................................................................. 10
3.6. TESTING CODE ...................................................................................................................................... 16

Week 3 Overview
This week we will introduce you to a set of principles for writing “good” code.
Writing code from scratch is often very time consuming. You should always try to make your
code reusable, so that the code can be used to solve similar problems with only minimal extra
effort. You should also try to reuse existing code where possible, rather than write highly-
specialised new code.
Writing code that follows the good practice principles that we discuss this week may sometimes
take longer than writing poor code, at least initially. However, it is a small investment of time
that can save you many hours later.
The principles that we discuss this week include:
• Writing code that can be reused or adapted easily.
• Using functions where appropriate to make your code modular and hide complexity.
• Documenting your code well enough that anyone can understand the general process
that it uses.
• Testing your code to ensure it produces the correct outputs

1
ENG1014 Engineering Numerical Analysis Course Notes

3.1. Hardcoding
Consider the following example:

Calculate the value of 𝒚(𝒙) = 𝒙𝟒 − 𝟒𝒙𝟑 + 𝟏𝟐𝒙𝟐 − 𝟐𝟎𝒙 when 𝒙 = 𝟑


Here are two pieces of code that perform that calculation:

x = 3 y = 3**4 - 4*3**3 + 12*3**2 - 20*3


y = x**4 - 4*x**3 + 12*x**2 - 20*x

At first glance, it might seem that the second example is more efficient; it’s only one line of code
instead of two. But what happens if we decide that we actually want to use x = 5? We would have
to go back and change four individual copies of that number. In contrast, we would only have to
change one copy of that number in the first example.

Calculate the value of 𝒚(𝒙) = 𝒙𝟒 − 𝟒𝒙𝟑 + 𝟏𝟐𝒙𝟐 − 𝟐𝟎𝒙 when 𝒙 = 𝟓

x = 5 y = 5**4 - 4*5**3 + 12*5**2 - 20*5


y = x**4 - 4*x**3 + 12*x**2 - 20*x

Chances are good that we might miss one copy, especially if the copies are not all in the one
line like they are here but are spread throughout a long piece of code. This would then cause a
mysterious problem in our code, which might take a long time to correct.
“Hardcoding” is the practice of writing a value directly into your code, rather than saving it as a
variable and then using the variable in any later calculations. It’s considered to be bad practice,
because it makes it more difficult to change your code later.

Any number that might be used more than once should be defined as a variable. Once
defined, it should be used by calling its name.

2
ENG1014 Engineering Numerical Analysis Course Notes

3.2. Functions
In 1.6. Python Built-in Functions, we introduced functions as groups of statements that together
perform a single task. We also introduced several built-in functions that are available in every
Python installation.
Python also allows you to create your own functions. Functions are modular as they can reuse
a pattern of code on different input values. This section will describe how to create and use
custom functions in your code.

3.2.1. When to use a function


Using functions appropriately is an important part of good coding practice.
Functions can be used to hide long, detailed pieces of code that perform conceptually simple
tasks. For example, we do not need to know all the steps that a computer takes to calculate the
cosine of an angle; we only need to know what the output is and what it means.
Functions allow us to perform a task repeatedly, often using slightly different conditions each
time, therefore aiding the adaptability of our code. If you need to perform a certain set of
calculations many times throughout a script, you could type it out every time as needed in the
script. For example, if we wanted to calculate sin(𝑥) × cos (𝑥) × tan(𝑥) multiple times
throughout a script, we could type it out multiple times:
y = 5
a1 = np.sin(y) * np.cos(y) * np.tan(y)
b1 = np.sin(y**2) * np.cos(y**2) * np.tan(y**2)
c1 = np.sin(y**3) * np.cos(y**3) * np.tan(y**3)
d1 = np.sin(y**5) * np.cos(y**5) * np.tan(y**5)
e1 = np.sin(y**4) * np.cos(y**4) * np.tan(y**4)
f1 = np.sin(y**3) * np.cos(y**3) * np.tan(y**3)
z1 = a1 + b1 + c1 + d1 + e1 + f1

In this example, we are calculating sin(𝑥) × cos (𝑥) × tan(𝑥) multiple times and just changing
the value of 𝑥 (i.e. changing 𝑥 to 𝑦, 𝑦 2, 𝑦 3, etc.). This is time consuming to do, but even more
time consuming if later on we want to change the equation slightly – e.g. if we found out that the
equation should actually be sin2 (𝑥 ) × tan (𝑥) . Imagine if you’d already written the whole
equation 100 times in your code!
We should always try to write code in a way that avoids this situation, by not repeating the same
code more than necessary. You can see that this is similar to hardcoding a variable, as we
discussed in 3.1. Hardcoding, but in this case we are hardcoding the function itself.
In this case, we can create a function to compute sin(𝑥) × cos (𝑥) × tan(𝑥). This function will
accept one input (𝑥) and return the result of sin(𝑥) × cos (𝑥) × tan(𝑥) as the output. We will use
this example to illustrate how functions can be useful.

3
ENG1014 Engineering Numerical Analysis Course Notes

3.2.2. Creating functions


User-defined functions allow you to create customised functions based on users' demands.
Like the built-in Python functions, user-defined functions can have one or more inputs and
outputs. You will sometimes see function inputs referred to as arguments (args) and outputs
referred to as return values.

There are three major parts of a function:


1. Function header – defines the function name and the input(s)
2. Statements – the reusable block of code
3. Return statement – defines the output(s)
Syntax:
def <function_name>(<arguments>) #function header
<statements>
return <return value(s)> #return statement

• function_name: name of the function, follows the same naming rules as variables
• arguments: as many inputs as you'd like, separated by commas. Note: Functions can
have no arguments. In that case, the parenthesis would just be empty.
• In theory, the return value does not need to be declared. In that case, Python will return
a None value, and you won't be able to use any results that have been calculated. It is
best practice to always declare the return value, even if the return value is None.

For our sin(𝑥) × cos (𝑥) × tan(𝑥) example, the sct() function below will accept one argument
(𝑥), calculate the desired output (sin(𝑥) × cos (𝑥) × tan(𝑥)), and return the result as the output.
def sct(in_val):
out_val = np.sin(in_val) * np.cos(in_val) * np.tan(in_val)
return out_val

3.2.3. Calling functions


Once we’ve defined the sct() function, we can now call it, in exactly the same way as we call
Python built-in functions:
y = 5
z2 = sct(y) + sct(y**2) + sct(y**3) + sct(y**5) + sct(y**4) + sct(y**3)

If you provide too few or too many arguments, the function won’t run.

4
ENG1014 Engineering Numerical Analysis Course Notes

3.2.4. Functions with multiple inputs


As previously mentioned, functions can have multiple inputs.
For example, we could create a function to calculate the paper surface area (𝐴) of a conical
paper cup given the radius of the base (𝑟) and the volume (𝑉).
The following functions will be used inside the function to calculate 𝐴:
3𝑉
ℎ= , 𝐴 = 𝜋𝑟 √𝑟 2 + ℎ2
𝜋𝑟 2
For our conical paper cup example, the conecup_calc() function below accepts V and r as
arguments and returns A:
def conecup_calc(V, r):
h = 3 * V / (np.pi * r**2)
A = np.pi * r * np.sqrt(r**2 + h**2)
return A

Unlike our sin(𝑥) × cos (𝑥) × tan(𝑥) example, when calling conecup_calc(), multiple
arguments need to be provided.
There are two methods of calling functions with multiple arguments: positional arguments and
keyword arguments. Additionally, optional arguments provide another method of both defining
and calling functions with multiple arguments.

Positional arguments
By default, when a function is called, the arguments are assigned to variables in the same order
that they are listed in the function definition.
For example:
• We can call conecup_calc(1,2) to calculate A with V = 1 and r = 2
• Calling conecup_calc(2,1) will return a different value for A than calling
conecup_calc(1, 2)

Keyword arguments
We can specify which arguments we are defining by specifying the name of the argument. If all
the names are specified, it does not matter which order the arguments are called in. However,
all positional arguments must be listed before any keyword arguments.
For example:
• We can call conecup_calc(V = 1, r = 2) to calculate A with V = 1 and r = 2
• We can call conecup_calc(r = 2, V = 1) to calculate A with V = 1 and r = 2
• Calling conecup_calc(V = 1, 2) will return an error, as all positional arguments
must be listed before any keyword arguments

5
ENG1014 Engineering Numerical Analysis Course Notes

Optional arguments
In some cases, a function might have been written in such a way that some arguments have
default values. Since these arguments have default values, these arguments are thus optional.
If you do not specify a value for that argument, the default value will be used. You will have seen
several functions with optional arguments in previous weeks.
For our conical paper cup example, the conecup_calc_2() function below accepts V and r as
arguments and returns A. However, this version has r as an optional argument with a default
value of 2:
def conecup_calc_2(V, r = 2):
h = 3 * V / (np.pi * r**2)
A = np.pi * r * np.sqrt(r**2 + h**2)
return A

For example:
• We can call conecup_calc(1) to calculate A with V = 1 and r = 2
• We can call conecup_calc(1,1) to override the default value and calculate A with V =
1 and r = 1

3.2.5. Functions with multiple outputs


The same concept of positional arguments applies to functions with multiple outputs; these are
returned in the same order they are listed.
For example, we could create a function to calculate the paper surface area (𝐴) and height (ℎ)
of a conical paper cup given the radius of the base (𝑟) and the volume (𝑉).
The conecup_calc_3() function below accepts V and r as arguments and returns A and h:
def conecup_calc_3(V, r):
h = 3 * V / (np.pi * r**2)
A = np.pi * r * np.sqrt(r**2 + h**2)
return A, h
In order to use the values conecup_calc_3() returns, we need to save them to variables.
For example, cone_cup_3(1,2) will return values of A=12.66… and h=0.24…:
c, d = conecup_calc_3(V = 1, r = 2)

The call above will create the values c and d, with values of 12.66… and h=0.24…, respectively.
There are a couple of incorrect methods of calling functions with multiple outputs that result in
the outputs being saved incorrectly. When you call functions with multiple outputs, ensure you
provide the correct number of variables to save those outputs to, in the correct order.

Refer to Example 1 from the W3 – Good Programming Practices – Course Notes Examples

6
ENG1014 Engineering Numerical Analysis Course Notes

3.2.6. Variable scope


Variable scope refers to the part of a program in which a particular variable can be accessed.
Variables created inside a function belong to the local scope of that function and are known as
local variables. They only exist while that function is running and thus can only be used inside
that function. Once a function has finished running, Python removes all variables that were
defined within it. This is why the values returned by functions are usually stored in a new variable
outside the function. Without that step, the function’s data would no longer be available after
the function has finished running. This means that we can potentially have two different
variables with the same name: one inside the function, and one outside the function.
Variables created outside all functions belong to the global scope and are known as global
variables. Functions can access global variables. However, depending on the data type and the
actions made to that variable inside the function, global variables may have different values
before and after running the function.
While functions can access global variables without them being passed as arguments to the
function, this is considered poor programming practice. Although the code may work when the
global variable is defined elsewhere in the same file, it may fail in other contexts if it is not
defined, or it is defined to a different variable. If you tried to import that function to a different
file, there could be an error either because the variable is not defined, or if it is, it has a
completely unexpected value.
It is safer, especially in larger programs, to pass all necessary variables as parameters to a
function, rather than relying on external definitions. It is also important to know which data types
and actions impact the values of global variables.

Refer to Example 2 from the W3 – Good Programming Practices – Course Notes Examples

7
ENG1014 Engineering Numerical Analysis Course Notes

3.2.7. Functions checklist


Here is a checklist of things to remember when creating and calling a user-defined function:
• Do place functions at the top of your script.
• Do not try to do multiple tasks in one function. Functions should have one single
responsibility. For example, you should not print or plot inside a function unless you are
specifically writing a printing or plotting function.
• Do not give functions names that are already used for built-in functions (e.g. print(),
type(), etc.).
• Do not give functions names of previously defined functions and variables (e.g. if you
have defined a variable as sqrt, you should not also name a function sqrt)
• Do give functions names that are self-documenting (e.g. conecup_calc() clearly
indicates that the function calculates some information about a cone cup).
• Do check that the function you want to call has already been defined in your script,
either explicitly (using def) or by being imported from the relevant module.
• Do specify all positional arguments when calling a function. Where relevant, optional
arguments should also be specified.
• Do include a list of inputs and outputs in the function documentation (refer to 3.5.1.
Docstrings). Include the order in which they are listed, and check this when you are
planning to call the function.
• Do call functions after creating them; creating a function does not run the function. To
use it, you must call it.

Complete Practice Questions 1-4 from the W3 – Good Programming Practices – Course
Notes Practice Questions

You should now be able to answer Part A from W3 – Weekly Quiz

3.3. Lambda Functions


Lambda functions are a special type of function. Lambda functions are a quick and simple way
to write functions on-the-fly. After lambda functions are defined, you call them in exactly the
same way you call regular functions.
Syntax:
function_name = lambda input1, input2: output
For example, our sct() example could be defined like this:
sct = lambda x: np.sin(x) * np.cos(x) * np.tan(x)

Some important features of lambda functions are:


• They are always defined within the same file where they are called; they can’t be
imported from a separate file.
• They are written as a single line of code.

8
ENG1014 Engineering Numerical Analysis Course Notes

• You can have multiple inputs and outputs, but only one expression.
• Most often, this type of function is used to represent mathematical expressions.
So far, it seems like the only differences between lambda functions and regular functions is a
list of things that lambda functions can’t do – they can’t be more than one line of code and can’t
be used in other files. So why can’t we define all our functions the normal way, using def?
The special power of a lambda function is that it allows you to define a function (A), and then
pass that function to another function (B), without specifying any values for the variables used
by A. In other words, the function itself can be treated like a variable. It can often be very
convenient to define lambda functions and then use them inside of calling other functions.
We will find this ability very useful later in the course when we discuss numerical methods, but
it can also be very helpful when representing mathematical expressions that rely on other
mathematical expressions.
For example, our conecup_calc() example could be defined like this:
conecup_h = lambda V, r: 3 * V / (np.pi * r**2)
conecup_A = lambda V, r: np.pi * r * np.sqrt(r**2 + conecup_h(V,r)**2)

Refer to Example 4 from the W3 – Good Programming Practices – Course Notes Examples

Complete Practice Questions 5-8 from the W3 – Good Programming Practices – Course
Notes Practice Questions

You should now be able to answer Part B from W3 – Weekly Quiz

3.4. Modules and Packages


In 1.7. Python Modules and Packages, we introduced modules as files that contain function and
variable definitions that can be imported into other programs to make use of their functionality.
We also covered how to import modules and packages, and how to use the dot operator to call
the functions and variables defined in those modules and packages.
From the last two weeks, you’d already be familiar with how NumPy allows us to do lots of
mathematical coding, particularly working with arrays, while Matplotlib provides a suite of
graphing tools. In fact, there are hundreds of thousands of collections of Python code that are
available for various specialised applications. This is one of the major strengths of the Python
language (although you should always be careful using code from unknown sources).
Python also allows user-defined modules to be created. This section will describe how to create
and use custom modules.
In this unit, we will be restricting ourselves to only using base Python, Matplotlib, and NumPy,
as well as a custom ENG1014 module.

9
ENG1014 Engineering Numerical Analysis Course Notes

To create a module, save the code you want in a file called module_name.py. The name of the
module will be the same as the name of the file. This name is used both to import the module,
and to access its contents.
It is important to note that whenever you import a file, you will run all the code in that file, in the
same way as if you just run that file on its own. This is how all the functions and variables that
you have defined in a different file can be brought into the file where you want to use them.
However, this also means that if you had some extra code that was not one of the function or
variable definitions you wanted to bring over, you would run all that code too. In general, you
should write modules that contain only the function and variable definitions you want to bring
over to other files.
We will now begin building the eng1014 module that you will need for the rest of semester. We
will also introduce a new “signpost” in the course notes to point you to the instructions you will
need to follow the instructions to create your own eng1014 module. For week 3, this is a file
called “W3 – Good Programming Practices – ENG1014 Module Instructions.ipynb”, which can
be opened using JupyterLab.

Complete Steps 1-3 from the W3 – Good Programming Practices – ENG1014 Module
Instructions

Complete Practice Questions 9-10 from the W3 – Good Programming Practices – Course
Notes Practice Questions

3.5. Documentation
In 2.1.7. Effectively communicating printed results, we mentioned that it is vital to follow good
practices to make your methods (code and written workings) able to be understood, used, and
modified by yourself and other people. Documentation is the process of writing about your
code, so that it is clear what your code does – how it works, what it is for and why it is written the
way it is.
There are two forms of documentation in Python:
• Docstrings – how to use code
• Comments – why and how code works

3.5.1. Docstrings
Docstrings are the documentation that explains how to use code and are for the users of your
code. In particular, docstrings are the documentation of functions. Docstrings start and ends
with triple quotes ("""). A docstring is placed immediately after the function definition.
All user-defined functions should have a docstring that includes the following information:
• Description of what the function does
• Description of the input argument and outputs

10
ENG1014 Engineering Numerical Analysis Course Notes

Syntax:
def <function_name>(<argument(s)>)
"""
<function description>

Args:
<arg 1>: <argument 1 description>
<arg 2>: <argument 2 description>

Returns:
<return value 1>: <return value 1 description>
<return value 2>: <return value 2 description>
"""
return <return value(s)>

For example, our sct() example should include the following function documentation:
def sct(inp_val):
"""
The sct function takes a number input and calculates the product
of the sine, cosine and tangent of the input

Args:
in_val: Input angle in radians

Returns:
out_val: The product of the sin, cos and tan values of in_val
"""
outp_val = np.sin(in_val) * np.cos(in_val) * np.tan(in_val)
return out_val

Once a function is created, calling help(function_name) will print the docstring.


For example, to print the docstring we created for our sct() example:
help(sct)

11
ENG1014 Engineering Numerical Analysis Course Notes

Additionally, there is often a way to read the function documentation in your IDE, as your IDE
will use the docstring to automatically create a tooltip with information about the function. In
JupyterLab, place your cursor on the function name and then type shift+tab. In Spyder, hover
your cursor over the function name, and click on the tooltip to expand for additional help.

3.5.2. Reading function documentation


For every programming language and non-user-defined modules you use, there will be
documentation available online. In this section, we will explore some of the documentation
available for Python and NumPy.
When you read documentation, you will often find that you only need a little bit of information,
and that you only need to read the whole thing when you have a reason to look for more detail.
Let’s have a look at the pow() function available in Python.

Figure 3.1 – Documentation of the pow() function1


Now let’s have a look at the numpy.exp() function available in NumPy. You’ll notice that the
layout of the page is a bit different, but that it mostly communicates the same types of
information as the Python documentation.

1
https://docs.python.org/3/library/functions.html#pow

12
ENG1014 Engineering Numerical Analysis Course Notes

Figure 3.2 – Documentation of the numpy.exp() function2

The official documentation that you will need for this unit can be found here:
• Python
• NumPy
• Matplotlib
While the official documentation is always good to understand, sometimes it is easier or more
helpful to look at unofficial sources, or search the internet to find what you want. Many websites
exist that document not only individual functions or libraries, but also specific problems that
you may have.

3.5.3. Commenting your code


We briefly introduced the idea of commenting your code in 1.5. Python Comments. We will
further explore how commenting your code can make your code easier to understand, use, and
modify by yourself and other people.
Comments explain why and how code works and are for the maintainers of your code. Well-
written code should be easy to read and understand. This allows other people (including
members of any teams you are part of) to use/adapt your code without needing to spend great

2
https://numpy.org/doc/stable/reference/generated/numpy.exp.html

13
ENG1014 Engineering Numerical Analysis Course Notes

effort studying how it works. This section will describe some common factors we generally
consider when discussing the documentation of a script file.
When writing any script, you should consider organising the script so that any user or other
programmers can conveniently read through the script and understand the purpose and
application of the code. Examine Figure 3.3 and ask yourself how much of the code you can
understand, and what features help/hinder your understanding.

Figure 3.3 – Example of poor documentation of a script

Comments should be written to explain the overall purpose of the code, rather than the
mechanics of how it works. You can add comments to provide reference information that you
would use in your code. Comments are also an excellent option to describe limitations and
mention needed improvements of any script version. For complicated programs, comments
can be just as critical as the code itself – especially when working within a team or when the
code must be checked by others.

14
ENG1014 Engineering Numerical Analysis Course Notes

Figure 3.4 - Example of improved documentation of a script (note that this could still be
improved significantly!)

15
ENG1014 Engineering Numerical Analysis Course Notes

Here are some guidelines on using comments to provide proper documentation of your code:
• Document your code as you write it (or even before you write it, to help with planning).
• Use plain English and keep the comments concise.
• Avoid redundant comments. For example, the following are not useful:
• x = y + 1 # x is y plus one (bad comment)
• temperature = 30 # the temperature is set to 30 (bad comment)
• You do not need to comment on every single line of your script. Provide comments on
different blocks of the scripts, such as:
• When defining variables
• When calculating using equations
• Explain the loops in the code
• The parameters/results/variables you are plotting
• Include some general information at the first line of the code:
• Your name and student ID number
• The date you created or last modified the script
• A short description of what the script does
Figure 3.4 provides an improved example of the code shown in Figure 3.3. Examine Figure 3.4
and ask yourself how much of the code you can now understand, and what features help your
understanding.

Complete Practice Question 11 from the W4 – Good Programming Practices – Course Notes
Practice Questions

3.6. Testing Code


When we write a piece of code, it is vital to test that the code is working as expected before it is
finalised. This is particularly important when we might use that piece of code again later, or
when the piece of code is just a small part of a much larger script.

3.6.1. Input testing


Often, a piece of code might be written so that it only works for particular inputs. For example,
here are some common cases:
• The code works on single numbers but doesn’t work if the input is an array
• The code works on 1D arrays but doesn't work if the input is a 2D array (and vice versa)
• Certain inputs need to be non-zero, positive/negative
• Certain inputs must be integers, floating-point numbers, or other data types
• Numbers must be entered with a particular set of units (e.g. a length in metres, not
centimetres or yards)
Ideally, the code should also check that these requirements are fulfilled, although this is not
always possible (e.g. the case of needing particular units). In cases where it is possible to check,

16
ENG1014 Engineering Numerical Analysis Course Notes

the code should print an error message whenever the checking process reveals that the function
has been run incorrectly. If it is not possible to check, then the limitations should be clearly
noted in the function’s comments.

3.6.2. Output testing


It is also important to confirm that the calculations in a piece of code are being performed
correctly. To do this, we must use some input data where we already know what the calculated
answer should be. Typically, this data is not random; it is carefully chosen (a) so that we test a
range of different potential problems, and (b) so we can most easily track down where any
problems might be. Good test data will often show you where an error is.
Generally, we want to test:
• “Normal” inputs – inputs which might be typical of the real inputs we want to analyse.
• Erroneous inputs – inputs that should not be used for calculations, that should be
detected and flagged by the code as being wrong.
• Boundary cases- inputs that are close to the boundary between being accepted and not
accepted. Values on both sides of the boundary (both “normal” and “erroneous”
boundary cases) should be checked.
Choosing good test data is a skill that is best learned through practice, and the set will be
different for each piece of code. Choosing a good test datapoint requires you to understand
what each particular piece of your code should be doing. While it’s not possible to make a
general list of “good” test data, here are some hints that are often a good place to start:
Example 1: For a function with one numerical input:
• “Normal” inputs: 0, 1, 2, 7, -1, 1.01, 0.01, π, 100, -100, 1000000
• Erroneous inputs: "a", True
• Boundary cases: [1,0,1], "1"
Reasoning: numerical inputs should work. "a" should fail. [1,0,1] and "1" are possible
boundary cases, and should deliberately be made to be valid or invalid inputs. True and False
often cause strange behaviour. Anything that is not a number should fail here!

Example 2: For a function with a list containing three inputs:


• “Normal” inputs: [0,0,0], [1,0,0], [0,1,0], [0,0,1], [1,1,1]
• Erroneous inputs: [1,0,1,1], [1], [1,0], [], 1, "123"
• Boundary cases: [[1],[2],[3]]
Reasoning: lists containing 3 elements should always work. Lists containing more or less than
3 elements should fail, as should standalone numbers. Empty lists sometimes cause strange
behaviour! Strings use indexing just like lists, also causing strange behaviour. [[1],[2],[3]]
is a 2D array and may also be a valid input if desired.

17
ENG1014 Engineering Numerical Analysis Course Notes

Example 3: For a function with an array containing 4 numerical inputs in a vector array:
• “Normal” inputs: np.array([1,0,1,1]), np.array([0,0,0,0]), np.array([-
1,0,1,21])
• Erroneous inputs:"abcd", 1, np.array([1,0,1]), np.array([1,0,1,1,1]),
[1,0,1,1,1], ["a","b","c","d"]
• Boundary cases: [1,0,1,1], np.array([[1],[0],[1],[1]]),
np.array(([1,0],[0,1]))
Reasoning: ndarrays with 4 inputs in a 1D list should always work here! Strings, standalone
numbers, and arrays that have more or less than 4 elements should be rejected. Lists containing
4 numbers can be made to work (or can be deliberately rejected), but lists containing anything
other than numbers are invalid inputs. If the column array is valid, should the 2D matrix
containing 4 elements also be valid? Output testing helps make sure that none of these edge
cases reach a user without being thoroughly documented!
Example 4: For a function with a string input:
• “Normal” inputs: "testing", "TESTING", "TESTing", "123", "abc ", " abc",
"testing input", "a", ""
• Erroneous inputs: ["testing","input2","input3"], 1
Reasoning: strings should always work in this function! Spaces can often cause issues in string
operations, especially trailing white spaces ("abc ", " abc"). Make sure the string is outputting
the case you’re expecting (uppercase, lowercase, or unchanged). Lists and numbers may sneak
through, so always check these. They usually should be failing!

Always test the obvious (normal) cases, as these need to work! Common variable types
(integers, floats, strings, lists, ndarrays) often will find their way into functions. Preparing and
testing for these ahead of time will often allow for quick recognition of these variable types
mistakenly being passed into your user-defined functions.

Complete Practice Questions 12-13 from the W4 – Good Programming Practices – Course
Notes Practice Questions

You should now be able to answer Part C from W3 – Weekly Quiz

18

You might also like