allpairspy

repository·master·Indexed 43 days ago

https://github.com/thombashi/allpairspy

A Python-based test combinations generator that implements the pairwise testing method to reduce the number of test cases required to cover variable interactions. It features the AllPairs class for generating combinations, support for filtering invalid combinations via filter_func, and integration with pytest.mark.parametrize for parameterized testing.

Tokens
1.5K
Snippets
5
Records
6
Agent score
5%

What's inside allpairspy

  1. Install allpairspy

    master

    You can install allpairspy using pip or via apt on Ubuntu systems using a PPA.

    Using pip:

    pip install allpairspy

    Using apt (Ubuntu):

    sudo add-apt-repository ppa:thombashi/ppa
    sudo apt update
    sudo apt install python3-allpairspy
    pip install allpairspy
  2. Integrate AllPairs with pytest for parameterized testing

    master

    You can use AllPairs to drive pytest.mark.parametrize. There are two main patterns depending on how you want to receive the data.

    Pattern 1: Value Matrix (Unpacking values) Pass the list of parameter names and a list comprehension that iterates over AllPairs to unpack the values directly into the test function arguments.

    Pattern 2: Named Access (Using OrderedDict) Pass an OrderedDict to AllPairs and use a single pair argument in your test function. This allows you to access values via pair.key_name.

    import pytest
    from allpairspy import AllPairs
    from collections import OrderedDict
    
    # Pattern 1: Unpacking values
    class TestUnpacked:
        @pytest.mark.parametrize(["brand", "os"], [
            list(p) for p in AllPairs([["X", "Y"], ["98", "XP"]])
        ])
        def test_logic(self, brand, os):
            assert True
    
    # Pattern 2: Using named tuples via OrderedDict
    class TestNamed:
        @pytest.mark.parametrize("pair", [
            [p] for p in AllPairs(OrderedDict({"brand": ["X", "Y"], "os": ["98", "XP"]}))
        ])
        def test_logic_named(self, pair):
            assert pair.brand in ["X", "Y"]
  3. Known issues and limitations

    master

    When using allpairspy, be aware of the following:

    • Filtering Performance: A poorly written filter_func may lead the generator to perform a full permutation of parameters, negating the efficiency of pairwise testing.
    • Optimization: The generated set is 'good enough' but may not be the absolute smallest possible set covering all pairs.
    • Performance Regression: Version 2.0+ may be slower due to the added capability of generating n-wise combinations.
  4. Filter invalid combinations using filter_func

    master

    You can restrict the generated combinations by providing a filtering function to the filter_func argument in the AllPairs constructor.

    Filtering Function Requirements:

    • Must return True if the combination is valid and False otherwise.
    • The row passed to the function can be incomplete (a subset of the full data). The generator uses this to prune the search space early.
    • The row is a list/tuple representing the current combination being evaluated.
    from allpairspy import AllPairs
    
    def is_valid_combination(row):
        n = len(row)
        # Example: Brand Y does not support Windows 98
        if n > 1:
            if "98" == row[1] and "Brand Y" == row[0]:
                return False
        return True
    
    parameters = [
        ["Brand X", "Brand Y"],
        ["98", "NT", "2000", "XP"],
        ["Internal", "Modem"]
    ]
    
    for i, pairs in enumerate(AllPairs(parameters, filter_func=is_valid_combination)):
        print(pairs)
  5. Use OrderedDict as a data source for named access

    master

    If you pass a collections.OrderedDict to the AllPairs constructor, the generated combinations will be returned as collections.namedtuple instances. This allows you to access parameter values by their keys instead of by index.

    from collections import OrderedDict
    from allpairspy import AllPairs
    
    parameters = OrderedDict({
        "brand": ["Brand X", "Brand Y"],
        "os": ["98", "NT", "2000", "XP"],
        "minute": [15, 30, 60],
    })
    
    for i, pairs in enumerate(AllPairs(parameters)):
        # Access via named attributes
        print(f"{i}: {pairs.brand}, {pairs.os}, {pairs.minute}")
  6. Generate pairwise test combinations with AllPairs

    master

    The AllPairs class generates a set of test combinations using the pairwise combinations method. This reduces the total number of combinations while ensuring most variable interactions are covered. It provides a Pythonic, iterator-style interface.

    Pass a list of lists (where each inner list represents the possible values for a parameter) to the AllPairs constructor.

    from allpairspy import AllPairs
    
    parameters = [
        ["Brand X", "Brand Y"],
        ["98", "NT", "2000", "XP"],
        ["Internal", "Modem"],
        ["Salaried", "Hourly", "Part-Time", "Contr."],
        [6, 10, 15, 30, 60],
    ]
    
    for i, pairs in enumerate(AllPairs(parameters)):
        print("{:2d}: {}".format(i, pairs))