Python Testing

Testing Notes

    1. Create a file to run your test on

    1. Import the unittest package and the function you want to test from your file

import unittest
from [file name] import [function name]
    1. Create a class that inherits from unittest.TestCase that will test your function

class TestFunction(unittest.TestCase):
    1. Create a method that tests a specific instance of the function

def test_example(self):
    1. Use the following methods to check if the expected output of a function matches with the corresponding input

  • .assertEqual(a, b) - Checks if a is equal to b

  • .assertNotEqual(a, b) - Checks if a is unequal to b

  • .assertTrue(x) - Checks if x is true

  • .assertFalse(x) - Checks if x is false

# Example using the "add()" function:
self.assertEqual(add(2,5), 7)
    1. To run through multiple instances, create a for loop and then create a subset using unittest’s built in method

for a in [2, 4, 6, -8, -10, -12]:
    with self.subTest(a=a):
        self.assertTrue(isEven(a))
    1. Once you’ve created all the methods to test all types of instances, run unittest.main in main

if __name__ == "__main__":
    unittest.main()

This should give you the results of the test and any errors that don’t match the intended result

AI Queue Management Example

class Queue:
    """
    A simple Queue implementation using a Python list.
    Operates on a First-In, First-Out (FIFO) principle.
    """

    def __init__(self):
        # The list will hold the queue elements.
        # The 'front' of the queue is index 0.
        self.items = []

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

    def enqueue(self, item):
        """Adds an item to the rear of the queue."""
        # Using insert(0, item) to add to the *end* for a list-based queue,
        # where the *front* will be the pop() end (index -1 or 0).
        # We'll use append() and pop(0) for clear FIFO with lists.
        # New items go to the end (rear).
        self.items.append(item)
        print(f"✅ Enqueued: '{item}'. Queue size: {self.size()}")

    def dequeue(self):
        """Removes and returns the item from the front of the queue (FIFO)."""
        if self.is_empty():
            print("❌ Error: Cannot dequeue. The queue is empty.")
            return None
        # pop(0) removes the item at the *front* (index 0)
        dequeued_item = self.items.pop(0)
        print(f"➡️ Dequeued: '{dequeued_item}'. Queue size: {self.size()}")
        return dequeued_item

    def peek(self):
        """Returns the item at the front of the queue without removing it."""
        if self.is_empty():
            print("👀 Peek: The queue is empty.")
            return None
        # The item at the front is at index 0
        return self.items[0]

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

    def display(self):
        """Displays the current state of the queue."""
        if self.is_empty():
            print("Queue: [Empty]")
        else:
            # Displaying from front to rear
            print(f"Queue (Front -> Rear): {self.items}")


if __name__ == '__main__':
    # --- Demonstration of the Queue ---

    print("--- Initializing Queue ---")
    # Create a new queue instance
    service_queue = Queue()

    # 1. Check initial state
    service_queue.display()
    print(f"Is the queue empty? {service_queue.is_empty()}")
    print("-" * 25)

    # 2. Enqueue some customers/elements
    print("--- Enqueue Operations ---")
    service_queue.enqueue("Customer A")
    service_queue.enqueue("Customer B")
    service_queue.enqueue("Customer C")
    service_queue.display()
    print("-" * 25)

    # 3. Peek at the front element
    print("--- Peek Operation ---")
    front_item = service_queue.peek()
    if front_item:
        print(f"📌 Next in line (Peek): '{front_item}'")
    service_queue.display() # Queue remains unchanged
    print("-" * 25)

    # 4. Dequeue elements (FIFO)
    print("--- Dequeue Operations (Serving Customers) ---")
    service_queue.dequeue() # Should be Customer A
    service_queue.dequeue() # Should be Customer B
    service_queue.display()
    print("-" * 25)

    # 5. Enqueue another element while others are waiting
    print("--- More Enqueue/Dequeue ---")
    service_queue.enqueue("Customer D")
    service_queue.display()
    service_queue.dequeue() # Should be Customer C (next FIFO)
    service_queue.dequeue() # Should be Customer D
    print("-" * 25)

    # 6. Attempt to dequeue from an empty queue
    print("--- Empty Queue Check ---")
    service_queue.dequeue()
    service_queue.display()

AI Testing Example

import unittest
# Import the Queue class from the separate queue implementation file
from queuemanagement import Queue 

class QueueTest(unittest.TestCase):
    """
    A testing suite for the Queue class using the unittest framework.
    """

    def setUp(self):
        """Set up a fresh Queue instance before each test method."""
        self.queue = Queue()

    def test_initial_state(self):
        """Test that a new queue starts empty and has size 0."""
        self.assertTrue(self.queue.is_empty(), "New queue should be empty.")
        self.assertEqual(self.queue.size(), 0, "New queue size should be 0.")

    def test_enqueue_and_size(self):
        """Test adding elements and verifying the size increases correctly."""
        self.queue.enqueue("Task 1")
        self.assertFalse(self.queue.is_empty(), "Queue should not be empty after enqueue.")
        self.assertEqual(self.queue.size(), 1, "Size should be 1 after one enqueue.")

        self.queue.enqueue("Task 2")
        self.assertEqual(self.queue.size(), 2, "Size should be 2 after two enqueues.")

    def test_dequeue_fifo_principle(self):
        """
        Crucial test: Verify that the First-In, First-Out (FIFO) principle is enforced.
        """
        # First In
        self.queue.enqueue("A") 
        self.queue.enqueue("B")
        self.queue.enqueue("C")
        
        # First Out
        dequeued_a = self.queue.dequeue()
        self.assertEqual(dequeued_a, "A", "The first element dequeued must be 'A' (FIFO).")
        self.assertEqual(self.queue.size(), 2, "Size should decrease after dequeue.")

        dequeued_b = self.queue.dequeue()
        self.assertEqual(dequeued_b, "B", "The second element dequeued must be 'B'.")

        dequeued_c = self.queue.dequeue()
        self.assertEqual(dequeued_c, "C", "The third element dequeued must be 'C'.")

        self.assertTrue(self.queue.is_empty(), "Queue should be empty after all items are removed.")

    def test_peek_functionality(self):
        """Test peek returns the front element without removing it."""
        self.queue.enqueue(10)
        self.queue.enqueue(20)

        # Peek at 10
        front_element = self.queue.peek()
        self.assertEqual(front_element, 10, "Peek should return the front element (10).")
        
        # Check that size and state are unchanged
        self.assertEqual(self.queue.size(), 2, "Size should remain 2 after peek.")
        self.assertEqual(self.queue.dequeue(), 10, "Dequeue should still return 10.")

    def test_dequeue_on_empty(self):
        """Test that dequeueing an empty queue returns None and doesn't crash."""
        result = self.queue.dequeue()
        self.assertIsNone(result, "Dequeue on empty queue should return None.")
        self.assertTrue(self.queue.is_empty(), "Dequeue on empty queue should not change state.")

    def test_peek_on_empty(self):
        """Test that peeking an empty queue returns None."""
        result = self.queue.peek()
        self.assertIsNone(result, "Peek on empty queue should return None.")
        self.assertTrue(self.queue.is_empty(), "Peek on empty queue should not change state.")
        
    def test_mixed_enqueue_and_dequeue(self):
        """Test a sequence of mixed operations to ensure consistency."""
        self.queue.enqueue("A")
        self.queue.dequeue() # A is removed
        
        self.queue.enqueue("B")
        self.queue.enqueue("C")
        self.assertEqual(self.queue.dequeue(), "B", "B should be the next item (FIFO).")
        
        self.queue.enqueue("D")
        self.assertEqual(self.queue.peek(), "C", "C should be the front item.")
        self.assertEqual(self.queue.size(), 2, "Size should be 2 (C and D).")


if __name__ == '__main__':
    # To run the tests, call: unittest.main()
    # We use exit=False here to allow execution in environments that might restrict sys.exit()
    print("Running Queue Unit Tests...\n" + "="*30)
    unittest.main(argv=['first-arg-is-ignored'], exit=False)
    print("\n" + "="*30)
    print("Test execution complete.")

AI Explanation

I used the standard unittest library to create a robust suite. Here is a breakdown of what each test case verifies:

    1. setUp(self): This method runs before every single test. It ensures we start with a brand-new, empty Queue instance (self.queue) for consistency.

    1. test_initial_state: Verifies that a newly created queue is correctly reported as empty and has a size of zero.

    1. test_enqueue_and_size: Confirms that the enqueue operation successfully adds an item, and the size() and is_empty() methods update their state correctly.

    1. test_dequeue_fifo_principle: This is the most critical test. It enqueues three elements (“A”, “B”, “C”) and checks that when dequeue is called, they are returned in the exact same order they were added (A then B then C), ensuring the FIFO behavior.

    1. test_peek_functionality: Ensures that peek() correctly identifies the front element but does not remove it, leaving the queue size and state unchanged.

    1. test_dequeue_on_empty: Handles the edge case where a user attempts to remove an item from an empty queue, verifying the function returns None and the program doesn’t crash.

    1. test_peek_on_empty: Handles the edge case where a user attempts to peek at an empty queue, verifying the function returns None.

    1. test_mixed_enqueue_and_dequeue: Tests a more realistic scenario involving alternating additions and removals to ensure the internal state remains consistent across complex operations.

Running Sum Class (Example Program)

class RunningSum:

    def __init__(self):
        self.total = 0

    def add(self, value):
        if type(value) == int:
            print("%s added!" % str(value))
            self.total += value
        elif type(value) == list:
            for a in value:
                if type(a) == int:  
                    print("%s added!" % str(a))
                    self.total += a
                else:
                    print("Error! Invalid Input for: %s" % str(a))
        else:
            print("Error! Invalid Input for: %s" % str(value))

    def subtract(self, value):
        if type(value) == int:
            print("%s subtracted" % str(value))
            self.total -= value
        elif type(value) == list:
            for a in value:
                if type(a) == int:  
                    print("%s subtracted" % str(a))
                    self.total -= a
                else:
                    print("Error! Invalid Input for: %s" % str(a))
        else:
            print("Error! Invalid Input for: %s" % str(value))
        
        if self.total < 0:
            print("Action hit 0! Total set to 0")
            self.total = 0

    def clear(self):
        print("Total Cleared.")
        self.total = 0

    def returnTotal(self):
        print("Current total: %s" % str(self.total))
        return self.total
    

if __name__ == '__main__':
    total1 = RunningSum()
    total1.returnTotal()
    print("-" *5)

    total1.add(3)
    total1.add(4)
    total1.add(8)
    total1.returnTotal()
    print("-" *5)

    total1.add([6,1,2,66,3,23,6,21,1])
    total1.returnTotal()
    print("-" *5)

    total1.subtract(5)
    total1.subtract(7)
    total1.subtract(2)
    total1.subtract(12)
    total1.subtract(3)
    total1.returnTotal()
    print("-" *5)

    total1.subtract([20,30,40])
    total1.returnTotal()
    print("-" *5)

    total1.subtract(1200)
    total1.returnTotal()
    print("-" *5)

    total1.add(3)
    total1.add(4)
    total1.add(8)
    total1.returnTotal()
    total1.clear()
    total1.returnTotal()

Testing Running Sum

import unittest
from runningsum import RunningSum


class TestFunction(unittest.TestCase):

    def setUp(self):
        self.run = RunningSum()

    def test_default_0(self):
        self.assertEqual(self.run.returnTotal(), 0)

    def test_add_int(self):
        self.run.add(6)
        self.assertEqual(self.run.returnTotal(), 6)

    def test_add_float(self):
        self.run.add(3/2)
        self.assertEqual(self.run.returnTotal(), 0)

    def test_add_error(self):
        self.run.add("21")
        self.assertEqual(self.run.returnTotal(), 0)

    def test_add_list(self):
        self.run.add([1,2,3,4,11])
        self.assertEqual(self.run.returnTotal(), 21)

    def test_add_list_error(self):
        self.run.add(["a", "b", "c", "d"])
        self.assertEqual(self.run.returnTotal(), 0)

    def test_add_list_mixed(self):
        self.run.add([21, 0.4, "oooooh", 2])
        self.assertEqual(self.run.returnTotal(), 23)

    def test_subtract_int(self):
        self.run.add(100)
        self.run.subtract(2)
        self.assertEqual(self.run.returnTotal(), 98)

    def test_subtract_float(self):
        self.run.add(100)
        self.run.subtract(979/10)
        self.assertEqual(self.run.returnTotal(), 100)

    def test_subtract_error(self):
        self.run.add(100)
        self.run.subtract("21")
        self.assertEqual(self.run.returnTotal(), 100)

    def test_subtract_list(self):
        self.run.add(100)
        self.run.subtract([1,2,3,4,5])
        self.assertEqual(self.run.returnTotal(), 85)

    def test_subtract_list_error(self):
        self.run.add(100)
        self.run.subtract(["a", "b", "c", "d"])
        self.assertEqual(self.run.returnTotal(), 100)

    def test_subtract_list_mixed(self):
        self.run.add(100)
        self.run.subtract([21, 0.4, "oooooh", 2])
        self.assertEqual(self.run.returnTotal(), 77)

    def test_subtract_below_0(self):
        self.run.add(100)
        self.run.subtract(200)
        self.assertEqual(self.run.returnTotal(), 0)

    def test_clear(self):
        self.run.add(100)
        self.run.clear()
        self.assertEqual(self.run.returnTotal(), 0)



if __name__ == '__main__':
    unittest.main()