0

I want to create a unit test for a function which sorts a list of objects according to some object attribute(s). Here is a code sample:

class Foo:
    """Custom data type Foo."""

    def __init__(self,
                 a: str,
                 b: int,
                 c: int,
                 d: int,
                 e: int,
                 f: int):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.e = e
        self.f = f
        self.g = c * d * e
        self.h = c * d

This is a part of the unit test function which produces the error:

from random import randint
...
class Test(TestCase):

def test_item_sort_foo(self):
    random_list = [Foo(str(randint(0, 1000)),
                        randint(0, 100),
                        randint(0, 100),
                        randint(0, 100),
                        randint(0, 100),
                        randint(0, 100))
                   for x in range(100)]
...

The error message is:

TypeError: 'Foo' object is not subscriptable. 

Despite reading this question, I cannot understand why Python cares if Foo is subscriptable since random_list already is. I'm trying to generate a list of random Foo items similarly to the answer here. I am puzzled because I already have a (working) class of the kind

from dataclasses import dataclass
from typing import List


@dataclass
class Bar:
    """Bar custom data type."""
    i: str
    j: List[Foo]

And I can type, e.g.,

f = Foo(...)
b = Bar("baz", [])
b.j.append(f)

So here are my questions:

  • Do elements of subscriptable objects also have to be subscriptable? Why?
  • What is the best way to generate random data with the properties from above for testing?
  • How do I apply this principle for my case?

EDIT: This is the complete unit testing function:

def test_item_sort_foo(self):
        random_list = [Foo(str(randint(0, 1000)),
                            randint(0, 100),
                            randint(0, 100),
                            randint(0, 100),
                            randint(0, 100),
                            randint(0, 100))
                       for x in range(100)]
        sorted_list = item_sort_foos(random_list)
        assert eq(sorted_list, sorted(random_list,
                                      key=itemgetter(4, 7),
                                      reverse=True))

And here is the code of the function which implements the logic:

def item_sort_foos(foos: List[Foo]) -> List[Foo]
    return sorted(foos,
                  key=attrgetter("e", "h"),
                  reverse=True)

EDIT2: The full trace is:

Error
Traceback (most recent call last):
  File "/opt/anaconda3/lib/python3.7/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/opt/anaconda3/lib/python3.7/unittest/case.py", line 628, in run
    testMethod()
  File ".../src/project/tests/test_orderings.py", line 89, in test_item_sort_foos
    reverse=True))
TypeError: 'Foo' object is not subscriptable

If you need any additional info, please request in the comments.

mushishi
  • 141
  • 1
  • 10

1 Answers1

2

random_list is a list of Foo objects. When you use a sort key of key=itemgetter(4, 7), you are trying to index the foo object itself. Its the same as

foo = Foo(...)
sort_key = foo[4], foo[7]

Likely you want to use key=attrgetter("e", "h") as in the item_sort_foos function you are testing.

But this is test code. item_sort_foos is a very small function and if all you do is put its guts in your unit test, you haven't tested much. Instead you should pass in some canned lists that you already know what the output is supposed to be. Compare those.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • Indeed, itemgetter was used wrongly. Probably a parentheses issue; will edit my q – mushishi Jun 20 '20 at 17:17
  • I don't get the "canned" part though - is a list with random elements not best practice? – mushishi Jun 20 '20 at 19:51
  • 1
    This is a unit test which is "given input A expect output B". If you are putting in random numbers then you don't really know what to expect unless you just implement the same algorithm a second time. You want multiple tests. What happens with zero items? 1 item? 5 items with known sorting? Several items that compare the same? These will all produce previously determined output. How does 100 items really test anything, especially when there isn't a predetermined output. – tdelaney Jun 20 '20 at 20:07