Testing
Testing
• You observe its behaviour to see whether or not your program is doing
what it is supposed to do.
• Tests pass if the behaviour is what you expect. Tests fail if the behaviour differs
from that expected.
• If your program does what you expect, this shows that for the inputs used, the
program behaves correctly.
• If these inputs are representative of a larger set of inputs, you can infer
that the program will behave correctly for all members of this larger input
set.
• If the behaviour of the program does not match the behaviour that you
expect, then this means that there are bugs in your program that need to
be fixed.
Functional testing
Test the functionality of the overall system. The goals of functional testing are to
discover as many bugs as possible in the implementation of the system and to provide
convincing evidence that the system is fit for its intended purpose.
User testing
Test that the software product is useful to and usable by end-users. You need to show
that the features of the system help users do what they want to do with the software.
You should also show that users understand how to access the software’s features
and can use these features effectively.
Security testing
Test that the software maintains its integrity and can protect user information from theft
and damage.
• The number of tests needed obviously depends on the size and the
functionality of the application.
• The process continues until you have created a complete system ready
for release.
Start
Unit
Testing
Release Feature
testing testing
System
testing
Unit testing
The aim of unit testing is to test program units in isolation. Tests should be designed to execute
all of the code in a unit at least once. Individual code units are tested by the programmer as
they are developed.
Feature testing
Code units are integrated to create features. Feature tests should test all aspects of a feature.
All of the programmers who contribute code units to a feature should be involved in its testing.
System testing
Code units are integrated to create a working (perhaps incomplete) version of a system. The
aim of system testing is to check that there are no unexpected interactions between the
features in the system. System testing may also involve checking the responsiveness, reliability
and security of the system. In large companies, a dedicated testing team may be responsible
for system testing. In small companies, this is impractical, so product developers are also
involved in system testing.
Release testing
The system is packaged for release to customers and the release is tested to check that it
operates as expected. The software may be released as a cloud service or as a download to be
installed on a customer’s computer or mobile device. If DevOps is used, then the development
team are responsible for release testing otherwise a separate team has that responsibility.
• If a program unit behaves as expected for a set of inputs that have some shared
characteristics, it will behave in the same way for a larger set whose members
share these characteristics.
• The equivalence partitions that you identify should not just include those
containing inputs that produce the correct values. You should also identify
‘incorrectness partitions’ where the inputs are deliberately incorrect.
1 2
Partition 4 where all inputs
share characteristic C4.
Some inputs also share
4 characteristics C3 or C5 but
Partition 3, where all not both
inputs share 3
characteristic C3. 5 Partition 5 where all
Some inputs also share inputs share characteristics
characteristic C4. C4 and C5. None share
characteristic C3
namex = r"^[a-zA-Z][a-zA-Z-']{1,39}$"
if re.match (namex, s):
if re.search ("'.*'", s) or re.search ("--", s):
return False
else:
return True
else:
return False
Program 9.1
A name checking
function
Correct names 1
The inputs only includes alphabetic characters and are between 2 and 40 characters long.
Correct names 2
The inputs only includes alphabetic characters, hyphens or apostrophes and are between
2 and 40 characters long.
Incorrect names 1
The inputs are between 2 and 40 characters long but include disallowed characters.
Incorrect names 2
The inputs include allowed characters but are either a single character or are more than
40 characters long.
Incorrect names 3
The inputs are between 2 and 40 characters long but the first character is a hyphen or an
apostrophe.
Incorrect names 4
The inputs include valid characters, are between 2 and 40 characters long, but include
either a double hyphen, quoted text or both.
Force errors
Choose test inputs that force the system to generate all error messages. Choose
test inputs that should generate invalid outputs.
Fill buffers
Choose test inputs that cause all input buffers to overflow.
Repeat yourself
Repeat the same test input or series of inputs several times.
Keep count
When dealing with lists and list transformation, keep count of the number of
elements in each list and check that these are consistent after each
transformation.
One is different
If your program deals with sequences, always test with sequences that have a
single value.
• For example, if your product has a feature that allows users to login using their
Google account, then you have to check that this registers the user correctly
and informs them of what information will be shared with Google.
• You may want to check that it gives users the option to sign up for email
information about your product.
• Interaction tests
• These test the interactions between the units that implement the feature. The developers of the
units that are combined to make up the feature may have different understandings of what is
required of that feature.
• These misunderstandings will not show up in unit tests but may only come to light when the
units are integrated.
• The integration may also reveal bugs in program units, which were not exposed by unit testing.
• Usefulness tests
• These test that the feature implements what users are likely to want.
• For example, the developers of a login with Google feature may have implemented an opt-out
default on registration so that users receive all emails from a company. They must expressly
choose what type of emails that they don’t want.
• What might be preferred is an opt-in default so that users choose what types of email they do
want to receive.
User registration
As a user, I want to be able to login without creating a new account so that I don’t
have to remember another login id and password.
Information sharing
As a user, I want to know what information you will share with other companies. I
want to be able to cancel my registration if I don’t want to share this information.
Email choice
As a user, I want to be able to choose the types of email that I’ll get from you
when I register for an account.
Incorrect credentials
Test that the error message and retry screen is displayed if the user inputs
incorrect Google credentials.
Shared information
Test that the information shared with Google is displayed, along with a cancel or
confirm option. Test that the registration is cancelled if the cancel option is
chosen.
Email opt-in
Test that the user is offered a menu of options for email information and can
choose multiple items to opt-in to emails. Test that the user is not registered for
any emails if no options are selected.
• System testing involves testing the system as a whole, rather than the
individual system features.
• Testing the system to make sure it operates in the expected way in the different
environments where it will be used.
• Using the scenario, you identify a set of end-to-end pathways that users
might follow when using the system.
Andrew and Maria have a two year old son and a four month old daughter. They live in
Scotland and they want to have a holiday in the sunshine. However, they are concerned
about the hassle of flying with young children. They decide to try a family holiday planner
product to help them choose a destination that is easy to get to and that fits in with their
childrens’ routines.
Maria navigates to the holiday planner website and selects the ‘find a destination’ page.
This presents a screen with a number of options. She can choose a specific destination
or can choose a departure airport and find all destinations that have direct flights from
that airport. She can also input the time band that she’d prefer for flights, holiday dates
and a maximum cost per person.
Edinburgh is their closest departure airport. She chooses ‘find direct flights’. The system
then presents a list of countries that have direct flights from Edinburgh and the days
when these flights operate. She selects France, Italy, Portugal and Spain and requests
further information about these flights. She then sets a filter to display flights that leave
on a Saturday or Sunday after 7.30am and arrive before 6pm.
She also sets the maximum acceptable cost for a flight. The list of flights is pruned
according to the filter and is redisplayed. Maria then clicks on the flight she wants. This
opens a tab in her browser showing a booking form for this flight on the airline’s website.
1. User inputs departure airport and chooses to see only direct flights. User quits.
2. User inputs departure airport and chooses to see all flights. User quits.
3. User chooses destination country and chooses to see all flights. User quits.
4. User inputs departure airport and chooses to see direct flights. User sets filter
specifying departure times and prices. User quits.
5. User inputs departure airport and chooses to see direct flights. User sets filter
specifying departure times and prices. User selects a displayed flight and clicks
through to airline website. User returns to holiday planner after booking flight.
• The fundamental differences between release testing and system testing are:
• Release testing tests the system in its real operational environment rather than in a
test environment. Problems commonly arise with real user data, which is sometimes
more complex and less reliable than test data.
• The aim of release testing is to decide if the system is good enough to release, not to
detect bugs in the system. Therefore, some tests that ‘fail’ may be ignored if these
have minimal consequences for most users.
• An executable test includes the input data to the unit that is being tested,
the expected result and a check that the unit returns the expected result.
• You run the test and the test passes if the unit returns the expected
result.
Test
report
• Arrange You set up the system to run the test. This involves defining the test
parameters and, if necessary, mock objects that emulate the functionality of
code that has not yet been developed.
• Action You call the unit that is being tested with the test parameters.
• Assert You make an assertion about what should hold if the unit being tested
has executed successfully. In Program 9.2, I use AssertEquals, which checks if
its parameters are equal.
• If you use equivalence partitions to identify test inputs, you should have
several automated tests based on correct and incorrect inputs from each
partition.
loader = unittest.TestLoader()
tests = loader.discover('.')
testRunner = unittest.runner.TextTestRunner(verbosity=2)
testRunner.run(tests)
Increased automation
Reduced costs
System
tests
Feature tests
Unit tests
• The feature tests can then access features directly through the API
without the need for direct user interaction through the system’s GUI.
Feature
API
tests
Feature 1 Feature 2
Feature 3 Feature 4
• System testing, which should follow feature testing, involves testing the
system as a surrogate user.
• You are looking for interactions between features that cause problems,
sequences of actions that lead to system crashes and so on.
• To avoid these problems, testing tools have been developed that can record a
series of actions and automatically replay these when a system is retested
System API
Test failure
Run all
automated tests
All tests pass
• This means you can be confident that your tests cover all of the code that has
been developed and that there are no untested code sections in the delivered
code. In my view, this is the most significant benefit of TDD.
• The tests act as a written specification for the program code. In principle
at least, it should be possible to understand what the program does by
reading the tests.
I focused on the tests rather than the problem I was trying to solve
A basic principle of TDD is that your design should be driven by the tests you have written. I
found that I was unconsciously redefining the problem I was trying to solve to make it easier
to write tests. This meant that I sometimes didn’t implement important checks, because it
was difficult to write tests in advance of their implementation.
I spent too much time thinking about implementation details rather than the
programming problem
Sometimes when programming, it is best to step back and look at the program as a whole
rather than focusing on implementation details. TDD encourages a focus on details that
might cause tests to pass or fail and discourages large-scale program revisions.
• The tests should demonstrate that the system can resist attacks on its
availability, attacks that try to inject malware and attacks that try to
corrupt or steal users’ data and identity.
• You may also use automated tools that scan your system to check for
known vulnerabilities, such as unused HTTP ports being left open.
• Based on the risks that have been identified, you then design tests and
checks to see if the system is vulnerable.
• Once you have identified security risks, you then analyze them to assess
how they might arise. For example, for the first risk in Table 9.11
(unauthorized attacker) there are several possibilities:
• The user has set weak passwords that can be guessed by an attacker.
• The system’s password file has been stolen and passwords discovered by
attacker.
• For example, you might run a test to check that the code that allows users to set
their passwords always checks the strength of passwords.
• Code reviews involve one or more people examining the code to check
for errors and anomalies and discussing issues with the developer.
• Code reviews complement testing. They are effective in finding bugs that
arise through misunderstandings and bugs that may only arise when
unusual sequences of code are executed.
Setup
review Check Prepare
Reviewer
code to-do list
Prepare
code Discussion
Make code
Write review changes
Distribute report Programmer
code/tests
Setup review
The programmer contacts a reviewer and arranges a review date.
Prepare code
The programmer collects the code and tests for review and annotates them with
information for the reviewer about the intended purpose of the code and tests.
Distribute code/tests
The programmer sends code and tests to the reviewer.
Check code
The reviewer systematically checks the code and tests against their
understanding of what they are supposed to do.
Discussion
The reviewer and programmer discuss the issues and agree on the actions to
resolve these.
Have all data errors been considered and tests written for them? (General)
It is easy to write tests for the most common cases but it is equally important to check that the
program won’t fail when presented with incorrect data.
• The aim of program testing is to find bugs and to show that a program does what
its developers expect it to do.
• Four types of testing that are relevant to software products are functional testing,
user testing, load and performance testing and security testing.
• Unit testing involves testing program units such as functions or class methods that
have a single responsibility. Feature testing focuses on testing individual system
features. System testing tests the system as a whole to check for unwanted
interactions between features and between the system and its environment.
• Identifying equivalence partitions, in which all inputs have the same characteristics,
and choosing test inputs at the boundaries of these partitions, is an effective way of
finding bugs in a program.
• Test automation is based on the idea that tests should be executable. You develop
a set of executable tests and run these each time you make a change to a system.
• Security testing may be risk driven where a list of security risks is used to
identify tests that may identify system vulnerabilities.