[Python] Problem using an Abstract Class with unittest

Discussão em 'Python' iniciado por Stack, Abril 13, 2025.

  1. Stack

    Stack Membro Participativo

    I have implemented a Stack in Python using "abstract classes" similarily to Java interfaces behavior. Now I'm trying to figure out how to solve a problem with my tests. Since I have to emulate Java structure here, I am using PyHamcrest because Hamcrest was being used in Java, as well as Base Matcher and a Supplier. I'm using unittest for the tests.

    I am using an abstract test file as a supplier. This file us supposed to be only executed from its subclasses, but never directly. I have 3 files in /src/test/stack:

    stack_test_case.py (this is the abstract class)

    import unittest
    from abc import ABC
    from copy import deepcopy
    from hamcrest import assert_that, is_
    from src.main.common.empty_exception import EmptyException
    from src.test.stack.test_is_equal_to_stack import is_equal_to_stack

    class AbstractStackTestCase(unittest.TestCase, ABC):

    def create_stack(self):
    raise NotImplementedError("Debe ser implementado por la subclase.")

    def create_stack_with(self, *values):
    stack = self.create_stack()
    for value in values:
    stack.push(value)
    return stack

    def test_is_empty_empty_stack(self):
    stack = self.create_stack()
    assert_that(stack.is_empty(), is_(True))

    def test_is_empty_non_empty_stack(self):
    stack = self.create_stack()
    stack_copy = deepcopy(stack)
    stack_copy.push(10)
    assert_that(stack_copy.is_empty(), is_(False))

    def test_size_empty_stack(self):
    stack = self.create_stack()
    assert_that(len(stack), is_(0))

    def test_size_non_empty_stack(self):
    stack = self.create_stack_with(10, 11, 12, 13, 14, 15)
    assert_that(len(stack), is_(6))

    def test_top_empty_stack(self):
    stack = self.create_stack()
    with self.assertRaises(EmptyException):
    _ = stack.top

    def test_top_non_empty_stack(self):
    stack = self.create_stack_with(10, 11, 12, 13, 14, 15)
    assert_that(stack.top, is_(15))

    def test_push_none(self):
    stack = self.create_stack()
    with self.assertRaises(ValueError):
    stack.push(None)

    def test_push_empty_stack(self):
    original = self.create_stack_with(10)
    expected = self.create_stack()
    expected.push(10)
    assert_that(original, is_(is_equal_to_stack(expected)))

    def test_push_non_empty_stack(self):
    original = self.create_stack_with(10, 11, 12, 13, 14, 15)
    expected = self.create_stack_with(10, 11, 12, 13, 14)
    expected.push(15)
    assert_that(original, is_(is_equal_to_stack(expected)))

    def test_pop_empty_stack(self):
    stack = self.create_stack()
    with self.assertRaises(EmptyException):
    stack.pop()

    def test_pop_non_empty_stack(self):
    expected = self.create_stack_with(10, 11, 12, 13, 14, 15)
    actual = self.create_stack_with(10, 11, 12, 13, 14, 15, 16)
    assert_that(actual.pop(), is_(16))
    assert_that(actual, is_(is_equal_to_stack(expected)))

    def test_clear(self):
    stack = self.create_stack_with(10, 11, 12, 13, 14, 15)
    stack.clear()
    assert_that(stack.is_empty(), is_(True))


    test_linked_stack_case.py

    import unittest
    from src.main.stack.linked_stack import LinkedStack
    from src.test.stack.stack_test_case import AbstractStackTestCase

    class TestLinkedStackCase(AbstractStackTestCase):
    def create_stack(self):
    return LinkedStack()

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


    test_is_equal_to_stack.py

    import unittest
    from typing import TypeVar, Generic
    from hamcrest import assert_that, is_
    from hamcrest.core.base_matcher import BaseMatcher
    from hamcrest.core.description import Description
    from copy import deepcopy

    from src.main.stack.stack import Stack
    from src.main.stack.linked_stack import LinkedStack

    T = TypeVar("T")

    class IsEqualToStack(BaseMatcher, Generic[T]):
    def __init__(self, expected: Stack[T]):
    self.expected = expected

    def _matches(self, actual: Stack[T]) -> bool:
    if len(self.expected) != len(actual):
    return False

    expected_copy = deepcopy(self.expected)
    actual_copy = deepcopy(actual)

    while not expected_copy.is_empty() and not actual_copy.is_empty():
    if expected_copy.pop() != actual_copy.pop():
    return False

    return expected_copy.is_empty() and actual_copy.is_empty()

    def describe_to(self, description: Description) -> None:
    description.append_text("equal stacks")

    def is_equal_to_stack(expected: Stack[T]) -> IsEqualToStack[T]:
    return IsEqualToStack(expected)

    class TestIsEqualToStackMatcher(unittest.TestCase):
    def test_equal_stacks(self):
    stack1 = LinkedStack()
    stack2 = LinkedStack()
    for val in [1, 2, 3]:
    stack1.push(val)
    stack2.push(val)

    matcher = is_equal_to_stack(stack1)
    assert_that(matcher._matches(stack2), is_(True))

    def test_unequal_stacks(self):
    stack1 = LinkedStack()
    stack2 = LinkedStack()
    for val in [1, 2, 3]:
    stack1.push(val)
    for val in [1, 2, 4]:
    stack2.push(val)

    matcher = is_equal_to_stack(stack1)
    assert_that(matcher._matches(stack2), is_(False))

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


    Now I'm running the following command from the base directory:

    python -m unittest discover -s src/test/stack -p "test_*.py" -v


    But the tests of the abstract test file are being executed since the NotImplementedError is being printed in the terminal.

    I have tried almost everything, I don't know how to solve this. I need the tests working and the test from the abstract test file being executed from the other two test files.

    Continue reading...

Compartilhe esta Página