Testing#
Learning Objectives#
Understand the purpose and importance of testing in software development
Identify the benefits of implementing tests in your code
Differentiate between functional and non-functional requirements
Apply different testing strategies, including unit testing and integration testing
Develop and automate tests using tools like
pytest
Purpose of Testing#
Testing is essential to ensure that the code performs its intended functions correctly. A single error, such as a misplaced character, can change the output significantly, potentially rendering research results unreliable.
Benefits of Testing#
Efficiency: Early detection of problems can save time and reduce the amount of effort needed to correct them later.
Automation and Independence: Tests can be run any time and can be automated with little overhead once you understand the process.
Code Quality: Writing tests often leads to cleaner, more modular code which is easier to test and maintain.
Types of Requirements#
As discussed earlier in this course, there are two types of requirements:
Functional Requirements: These define what the software should do.
Non-Functional Requirements: These define how the software should behave, covering aspects like performance, security, and usability.
Testing Strategies#
Unit Testing#
Unit testing involves testing the smallest parts of the software, such as functions, in isolation to ensure they work correctly. Each test should focus on a single aspect to simplify troubleshooting. Tests should cover all potential inputs and outputs, ensuring each unit functions as expected.
Integration Testing#
Integration testing checks the combined functionality of multiple units to identify issues in their interaction. This ensures that different components of the software work together as expected. Units may be tested in combinations or all at once, especially important in collaborative environments.
Remember it is better to write some tests rather than none. Using testing frameworks like pytest can simplify the creation and execution of tests.
Practical Exercise: Testing the Fibonacci Function#
Objective#
Reflect on and devise tests for a function that returns the Fibonacci sequence, rather than printing it. A Python function to generate the Fibonacci sequence is given below.
Test Cases to Consider#
Correctness: Ensure the function returns a list of integers.
Sequence Accuracy: Verify that the sequence length is correct, and each integer follows the Fibonacci rule (i.e. each number is the sum of the two preceding numbers).
Edge Cases: Test minimum sequence length and handle invalid inputs (e.g. sequence lengths less than 3).
Exercise Code#
import typing
def generate_fibonacci(sequence_length: int) -> typing.List[int]:
"""
Generates the Fibonacci sequence up to the required sequence length.
Args:
`sequence_length` (int): Required number of Fibonacci numbers.
Returns:
`sequence` (list): Fibonacci sequence.
"""
if sequence_length < 3:
raise ValueError("Minimum sequence length is 3.")
sequence = [0, 1]
sequence_length -= 2
while sequence_length > 0:
sequence.append(sequence[-2] + sequence[-1])
sequence_length -= 1
return sequence
Example Tests
def test_ValueError():
"""Test the `ValueError` is thrown under appropriate conditions."""
try:
generate_fibonacci(2)
print("test_ValueError Failed: No ValueError for sequence length < 3")
except ValueError:
print("test_ValueError Passed")
def test_return_type():
"""Test a list of integers is returned."""
sequence = generate_fibonacci(10)
if isinstance(sequence, list) and all(isinstance(element, int) for element in sequence):
print("test_return_type Passed")
else:
print("test_return_type Failed: Return type is not list of integers")
def test_sequence_length():
"""Test the sequence returned is the correct length."""
try:
sequence = generate_fibonacci(3) # Shortest valid sequence.
assert len(sequence) == 3
sequence = generate_fibonacci(10)
assert len(sequence) == 10
print("test_sequence_length Passed")
except AssertionError:
print("test_sequence_length Failed: Incorrect sequence length")
def test_elements_correct():
"""
Test the sequence is the Fibonacci sequence
(i.e. the elements are correct).
"""
sequence_length = 5
sequence = generate_fibonacci(sequence_length)
try:
for n in range(2, sequence_length):
assert sequence[n] == sequence[n - 1] + sequence[n - 2]
print("test_elements_correct Passed")
except AssertionError:
print("test_elements_correct Failed: Incorrect Fibonacci sequence elements")
# Run the individual function tests
test_ValueError()
test_return_type()
test_sequence_length()
test_elements_correct()
Exercise: Automating Testing#
Explore the use of pytest to run the tests that you have developed.
Adapted Content From:
The Turing Way (Copyright © The Turing Way Community) (CC-BY licence)