Module pedantic.tests.test_generic_mixin

Expand source code
import unittest
from typing import TypeVar, Generic, List, Type

from pedantic import GenericMixin

T = TypeVar('T')
U = TypeVar('U')


class TestGenericMixin(unittest.TestCase):
    def test_single_type_var(self):
        class Foo(Generic[T], GenericMixin):
            value: T

        foo = Foo[str]()
        assert foo.type_var == str
        assert foo.type_vars == {T: str}

        invalid = Foo()

        with self.assertRaises(expected_exception=AssertionError) as err:
            invalid.type_var

        assert f'You need to instantiate this class with type parameters! Example: Foo[int]()' in err.exception.args[0]

    def test_multiple_type_vars(self):
        class Foo(Generic[T, U], GenericMixin):
            value: T
            values: List[U]

        foo = Foo[str, int]()

        with self.assertRaises(expected_exception=AssertionError) as err:
           foo.type_var

        self.assertEqual(err.exception.args[0], 'You have multiple type parameters. '
                                                'Please use "type_vars" instead of "type_var".')

        assert foo.type_vars == {T: str, U: int}

        invalid = Foo()

        with self.assertRaises(expected_exception=AssertionError) as err:
            invalid.type_var

        assert f'You need to instantiate this class with type parameters! Example: Foo[int]()' in err.exception.args[0]

    def test_non_generic_class(self):
        class Foo(GenericMixin):
            value: int

        invalid = Foo()

        with self.assertRaises(expected_exception=AssertionError) as err:
            invalid.type_var

        self.assertEqual(err.exception.args[0], f'Foo is not a generic class. To make it generic, declare it like: '
                                                f'class Foo(Generic[T], GenericMixin):...')

    def test_call_type_var_in_constructor(self):
        class Foo(Generic[T], GenericMixin):
            def __init__(self) -> None:
                self.x = self.type_var()

        with self.assertRaises(expected_exception=AssertionError) as err:
            Foo[str]()

        assert 'make sure that you do not call this in the __init__() method' in err.exception.args[0]

    def test_subclass_set_type_variable(self):
        class Gen(Generic[T], GenericMixin):
            def __init__(self, value: T) -> None:
                self.value = value

            def get_type(self) -> dict[TypeVar, Type]:
                return self.type_vars

        class MyClass(Gen[int]):
            pass

        bar = Gen[int](value=4)
        assert bar.get_type() == {T: int}

        foo = MyClass(value=4)
        assert foo.get_type() == {T: int}

    def test_subclass_with_multiple_parents(self):
        class Gen(Generic[T], GenericMixin):
            def __init__(self, value: T) -> None:
                self.value = value

            def get_type(self) -> dict[TypeVar, Type]:
                return self.type_vars

        class MyMixin:
            value = 42

        class MyClass(MyMixin, Gen[int]):
            pass

        bar = Gen[int](value=4)
        assert bar.get_type() == {T: int}

        foo = MyClass(value=4)
        assert foo.get_type() == {T: int}

Classes

class TestGenericMixin (methodName='runTest')

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the init method, the base class init method must always be called. It is important that subclasses should not change the signature of their init method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes: * failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'. * longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed. * maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.

Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.

Expand source code
class TestGenericMixin(unittest.TestCase):
    def test_single_type_var(self):
        class Foo(Generic[T], GenericMixin):
            value: T

        foo = Foo[str]()
        assert foo.type_var == str
        assert foo.type_vars == {T: str}

        invalid = Foo()

        with self.assertRaises(expected_exception=AssertionError) as err:
            invalid.type_var

        assert f'You need to instantiate this class with type parameters! Example: Foo[int]()' in err.exception.args[0]

    def test_multiple_type_vars(self):
        class Foo(Generic[T, U], GenericMixin):
            value: T
            values: List[U]

        foo = Foo[str, int]()

        with self.assertRaises(expected_exception=AssertionError) as err:
           foo.type_var

        self.assertEqual(err.exception.args[0], 'You have multiple type parameters. '
                                                'Please use "type_vars" instead of "type_var".')

        assert foo.type_vars == {T: str, U: int}

        invalid = Foo()

        with self.assertRaises(expected_exception=AssertionError) as err:
            invalid.type_var

        assert f'You need to instantiate this class with type parameters! Example: Foo[int]()' in err.exception.args[0]

    def test_non_generic_class(self):
        class Foo(GenericMixin):
            value: int

        invalid = Foo()

        with self.assertRaises(expected_exception=AssertionError) as err:
            invalid.type_var

        self.assertEqual(err.exception.args[0], f'Foo is not a generic class. To make it generic, declare it like: '
                                                f'class Foo(Generic[T], GenericMixin):...')

    def test_call_type_var_in_constructor(self):
        class Foo(Generic[T], GenericMixin):
            def __init__(self) -> None:
                self.x = self.type_var()

        with self.assertRaises(expected_exception=AssertionError) as err:
            Foo[str]()

        assert 'make sure that you do not call this in the __init__() method' in err.exception.args[0]

    def test_subclass_set_type_variable(self):
        class Gen(Generic[T], GenericMixin):
            def __init__(self, value: T) -> None:
                self.value = value

            def get_type(self) -> dict[TypeVar, Type]:
                return self.type_vars

        class MyClass(Gen[int]):
            pass

        bar = Gen[int](value=4)
        assert bar.get_type() == {T: int}

        foo = MyClass(value=4)
        assert foo.get_type() == {T: int}

    def test_subclass_with_multiple_parents(self):
        class Gen(Generic[T], GenericMixin):
            def __init__(self, value: T) -> None:
                self.value = value

            def get_type(self) -> dict[TypeVar, Type]:
                return self.type_vars

        class MyMixin:
            value = 42

        class MyClass(MyMixin, Gen[int]):
            pass

        bar = Gen[int](value=4)
        assert bar.get_type() == {T: int}

        foo = MyClass(value=4)
        assert foo.get_type() == {T: int}

Ancestors

  • unittest.case.TestCase

Methods

def test_call_type_var_in_constructor(self)
Expand source code
def test_call_type_var_in_constructor(self):
    class Foo(Generic[T], GenericMixin):
        def __init__(self) -> None:
            self.x = self.type_var()

    with self.assertRaises(expected_exception=AssertionError) as err:
        Foo[str]()

    assert 'make sure that you do not call this in the __init__() method' in err.exception.args[0]
def test_multiple_type_vars(self)
Expand source code
def test_multiple_type_vars(self):
    class Foo(Generic[T, U], GenericMixin):
        value: T
        values: List[U]

    foo = Foo[str, int]()

    with self.assertRaises(expected_exception=AssertionError) as err:
       foo.type_var

    self.assertEqual(err.exception.args[0], 'You have multiple type parameters. '
                                            'Please use "type_vars" instead of "type_var".')

    assert foo.type_vars == {T: str, U: int}

    invalid = Foo()

    with self.assertRaises(expected_exception=AssertionError) as err:
        invalid.type_var

    assert f'You need to instantiate this class with type parameters! Example: Foo[int]()' in err.exception.args[0]
def test_non_generic_class(self)
Expand source code
def test_non_generic_class(self):
    class Foo(GenericMixin):
        value: int

    invalid = Foo()

    with self.assertRaises(expected_exception=AssertionError) as err:
        invalid.type_var

    self.assertEqual(err.exception.args[0], f'Foo is not a generic class. To make it generic, declare it like: '
                                            f'class Foo(Generic[T], GenericMixin):...')
def test_single_type_var(self)
Expand source code
def test_single_type_var(self):
    class Foo(Generic[T], GenericMixin):
        value: T

    foo = Foo[str]()
    assert foo.type_var == str
    assert foo.type_vars == {T: str}

    invalid = Foo()

    with self.assertRaises(expected_exception=AssertionError) as err:
        invalid.type_var

    assert f'You need to instantiate this class with type parameters! Example: Foo[int]()' in err.exception.args[0]
def test_subclass_set_type_variable(self)
Expand source code
def test_subclass_set_type_variable(self):
    class Gen(Generic[T], GenericMixin):
        def __init__(self, value: T) -> None:
            self.value = value

        def get_type(self) -> dict[TypeVar, Type]:
            return self.type_vars

    class MyClass(Gen[int]):
        pass

    bar = Gen[int](value=4)
    assert bar.get_type() == {T: int}

    foo = MyClass(value=4)
    assert foo.get_type() == {T: int}
def test_subclass_with_multiple_parents(self)
Expand source code
def test_subclass_with_multiple_parents(self):
    class Gen(Generic[T], GenericMixin):
        def __init__(self, value: T) -> None:
            self.value = value

        def get_type(self) -> dict[TypeVar, Type]:
            return self.type_vars

    class MyMixin:
        value = 42

    class MyClass(MyMixin, Gen[int]):
        pass

    bar = Gen[int](value=4)
    assert bar.get_type() == {T: int}

    foo = MyClass(value=4)
    assert foo.get_type() == {T: int}