Parameterized testing: You want Inputs and Outputs
Is your test in the business of calculating the expected output from the inputs?
Then your test is duplicating the logic from the code under test.
There is a reason why too much logic in tests is frowned upon: If you implement the same logic twice, you are prone to repeat the same mistakes in the test which you already made in the real implementation.
for c in [
Case(socket_type=SOCK_STREAM, fruit="Apple"),
Case(socket_type=SOCK_STREAM, fruit="Orange"),
Case(socket_type=SOCK_DGRAM, fruit="Apple"),
Case(socket_type=SOCK_DGRAM, fruit="Orange"),
]:
# Oh no, surprise logic in the test!
# This becomes hard to find when the test gets longer.
expected = 1
if c.socket_type == SOCK_STREAM and c.fruit == "Orange":
expected = 0 # Expecting nothing in this case
self.assertEqual(expected, operation(c.socket_type, c.fruit))
Better is to flatten out the expected results into the test table and remove that logic from the test:
for c in [
Case(socket_type=SOCK_STREAM, fruit="Apple", expected=1),
Case(socket_type=SOCK_STREAM, fruit="Orange", expected=0), # special case
Case(socket_type=SOCK_DGRAM, fruit="Apple", expected=1),
Case(socket_type=SOCK_DGRAM, fruit="Orange", expected=1),
]:
self.assertEqual(c.expected, operation(c.socket_type, c.fruit))
A non-trivial real-world example (from the Go-Landlock library).
This is one of these articles that I’m writing just so that I can point to it later. In a code review. :)