Testing

Unit Testing in Python

Queue Management Program (Python)

Here is a simple class that implements a basic First-In, First-Out (FIFO) queue using Python’s built-in list structure.

class SimpleQueueManager:
    """
    A simple manager for a FIFO (First-In, First-Out) queue.
    """
    def __init__(self):
        # The list acts as the queue, with the 'front' being index 0.
        self.queue = []

    def enqueue(self, item):
        """
        Adds an item to the end (rear) of the queue.
        """
        self.queue.append(item)
        return f"'{item}' added to the queue."

    def dequeue(self):
        """
        Removes and returns the item from the front of the queue.
        Raises an IndexError if the queue is empty.
        """
        if not self.is_empty():
            # Use pop(0) to remove the first element (FIFO)
            return self.queue.pop(0)
        else:
            return "Error: Queue is empty."

    def peek(self):
        """
        Returns the item at the front of the queue without removing it.
        """
        if not self.is_empty():
            return self.queue[0]
        else:
            return "Queue is empty."

    def size(self):
        """
        Returns the current number of items in the queue.
        """
        return len(self.queue)

    def is_empty(self):
        """
        Checks if the queue is currently empty.
        """
        return self.size() == 0

# Example Usage:
  q = SimpleQueueManager()
  q.enqueue("Customer A")
  q.enqueue("Customer B")
  print(q.size())    # Output: 2
  print(q.peek())    # Output: Customer A
  print(q.dequeue()) # Output: Customer A
  print(q.dequeue()) # Output: Customer B
  print(q.dequeue()) # Output: Error: Queue is empty.

—

Testing Suite for the Queue Manager Program

A comprehensive testing suite for the SimpleQueueManager should aim for 100% code coverage, testing both the expected (happy path) and edge case (error path) behaviors of every method. We’ll use Python’s built-in unittest framework for this.

Key Testing Principles

  • Isolation: Tests should be independent; running one test shouldn’t affect the result of another.

  • Atomicity: Each test should focus on a single piece of functionality.

  • Black Box Testing: Focus on what the class should do, not how it does it (e.g., test dequeue() to see if it returns the correct item, not how it uses pop(0)).

The Test Suite Explanation

Test Case Category

Methods Covered

Description of Test

Expected Outcome

Basic Operations

enqueue(), dequeue()

Test adding one item, then removing it.

dequeue() returns the item added.

FIFO Integrity

enqueue(), dequeue()

Add multiple items (A, B, C) and ensure they are removed in the correct order (A, then B, then C).

Items are dequeued in the order they were enqueued.

Empty State (Edge Case)

is_empty(), size()

Test a newly instantiated queue.

is_empty() returns True, size() returns 0.

Full/Empty Cycle

All

Add items, empty the queue, then add new items.

The queue state is correctly reset, and new operations work as expected.

peek() Functionality

peek()

Check peek() after adding an item to ensure it returns the item but doesn’t change the size or remove the item.

peek() returns the correct item, and size() remains the same.

Error Handling (Edge Case)

dequeue(), peek()

Try to dequeue() or peek() an already empty queue.

Both methods return the “Queue is empty” or equivalent error message.

—

factorial Demonstration Program

To demonstrate understanding of testing, I’ll create a simple function to calculate the factorial of a number, along with a complete test suite for it using Python’s unittest.

The Simple Program: Factorial Calculator

def factorial(n):
    """
    Calculates the factorial of a non-negative integer n (n!).
    Raises a ValueError for negative numbers or non-integers.
    """
    # Type and Value Guardrails
    if not isinstance(n, int) or n < 0:
        raise ValueError("Factorial is only defined for non-negative integers.")

    # Base Case: 0! = 1
    if n == 0:
        return 1

    # Iterative Calculation
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

The Test Suite for Factorial Calculator

This test suite covers the base case, positive integers, and the required error handling.

import unittest
# Assume the factorial function is imported or defined above

class TestFactorial(unittest.TestCase):
    """
    Test suite for the factorial function.
    """

    def test_base_case_zero(self):
        """
        Test the factorial of 0, which should be 1.
        """
        # Arrange, Act, Assert (AAA)
        self.assertEqual(factorial(0), 1, "0! should be 1")

    def test_positive_integers(self):
        """
        Test common positive integer factorials (e.g., 1!, 5!).
        """
        # 1! = 1
        self.assertEqual(factorial(1), 1)
        # 5! = 120 (5*4*3*2*1)
        self.assertEqual(factorial(5), 120)
        # 7! = 5040
        self.assertEqual(factorial(7), 5040)

    def test_large_integer(self):
        """
        Test a larger number to ensure calculation scale is correct.
        10! = 3,628,800
        """
        self.assertEqual(factorial(10), 3628800)

    def test_negative_input(self):
        """
        Test that calling with a negative number raises a ValueError.
        """
        # assertRaises checks if the expected exception is raised
        with self.assertRaises(ValueError):
            factorial(-1)
        with self.assertRaises(ValueError):
            factorial(-5)

    def test_non_integer_input(self):
        """
        Test that calling with non-integer types (float, string) raises a ValueError.
        """
        with self.assertRaises(ValueError):
            factorial(4.5)
        with self.assertRaises(ValueError):
            factorial("a string")
        with self.assertRaises(ValueError):
            factorial(None)

# To run the tests, you would typically use:
# if __name__ == '__main__':
#     unittest.main()