Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can I test several cases for the same function without duplicating test logic?

Quick answer

Put each input and expected result in a case list, then loop over it inside one unittest test method. Wrap each assertion in self.subTest(...) and give every case a useful name. Python’s unittest subtests display supplied messages or parameters when a case fails, helping identify the failing input without repeating the assertion [S1].

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I have a function and several input/expected-output pairs. How do I avoid writing a separate test method with the same assertion for every pair?

Geez

Store the cases as data, then run the same assertion for each one in a loop. With unittest, put self.subTest(...) around the assertion. A subtest can identify the iteration by displaying its supplied message or parameters when it fails [S1].

For example, suppose the function should report whether a number is even. The case names below are descriptive labels, not special unittest syntax:

import unittest


def is_even(number):
    return number % 2 == 0


class IsEvenTests(unittest.TestCase):
    def test_examples(self):
        cases = [
            ("negative even", -4, True),
            ("zero", 0, True),
            ("positive odd", 3, False),
        ]

        for name, value, expected in cases:
            with self.subTest(case=name, value=value):
                self.assertEqual(is_even(value), expected)


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

There is one assertion to maintain, while the inputs and expected results stay visible as a short list.

Follow-up question

What makes a case name useful when a test fails?

Geez

Name the condition the example is meant to represent, and include a relevant input as a subtest parameter when that helps distinguish cases. For instance, case="negative even" communicates more than case="case 1"; value=-4 makes the input explicit too.

Keep labels short and specific: empty input, one item, or mixed case are more useful than edge case. If two examples have the same label, add a distinguishing value or another parameter. The goal is to make the failing iteration recognizable without having to count its position in the list.

Follow-up question

Should I put every scenario into this one loop, and what if the function needs more than one input?

Geez

Use the loop for cases that share the same basic check: provide inputs and compare the result with an expected value. If a scenario needs a different kind of assertion or setup, a separate test method can make its purpose clearer. Avoid turning the case list into a complicated mini-framework.

For a function with two inputs, add both to each case tuple and pass both into the function. For example, a case could be ("small addition", 2, 3, 5), unpacked as name, left, right, expected; then call add(left, right) inside the subtest. Keep the names and values aligned so each row is easy to inspect.

Follow-up question

How can I keep the tests easy to update and understand later?

Geez

Put the cases near the test that uses them, choose names that describe the behavior, and keep each row in the same order: label, inputs, expected result. When adding a case, check that its expected result reflects the behavior you intend to test rather than simply copying the function’s current output.

A subtest helps identify a failed iteration; it does not establish that your chosen examples cover every behavior or prove the function is correct. Subtests display supplied messages or parameters when an iteration fails [S1]. Choose the cases based on the function’s intended behavior.

A practical checklist

  • Write down each input and its expected result before adding it to the loop.

  • Give every case a short label that describes its purpose.

  • Include useful input values in self.subTest(...) parameters.

  • Keep one shared assertion for cases that check the same kind of behavior.

  • Use a separate test method when a scenario needs a different assertion or setup.

A few more useful answers

Will a failure show which input caused it?

The subtest’s supplied message or parameters are displayed when that subtest fails [S1]. Include a meaningful case label and relevant input values to make the failure easier to recognize.

Do subtests tell me whether I have enough test cases?

No. They help identify which listed iteration failed, but they do not determine whether your examples cover the function’s intended behavior. Choose cases that reflect the behaviors you need to check.

Sources

References used for this guide.

  1. [S1] Python unittest: Distinguishing test iterations with subtests
    docs.python.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work