A day of humble micro-testing – automatic generation of orthogonal method test cases

Foreword

In the process of work, after we come into contact with the requirements, the first priority is to be familiar with the requirements and output test cases. For the input parameter test of the interface test, we need to verify the combination of a large number of input parameters. At this time, we usually use the orthogonal method to design test cases. , while reducing the number of test cases, while ensuring the effective coverage of test cases.

Orthogonal method experiment

Orthogonal test method is an experimental method for analyzing multi-factors and multi-levels. It uses orthogonal tables to design experiments, and replaces comprehensive experiments with a small number of experiments.

In an experiment, the quantity that affects the experimental result is called the experimental factor (factor), referred to as factor. Factors can be understood as independent variables in the experimental process, and experimental results can be seen as a function of factors. During the experiment, each factor can be in a different state or state, and the state or state of the factor is called the level of the factor, referred to as the level.

for example:

There are 2 classes in the communication department of a university. I just finished a certain course. I want to analyze the score distribution of this course, the ratio of male to female or the ratio of classes in the communication department based on the three query conditions of “gender”, “class” and “grade”. To perform a personnel query:

Query based on “gender” = “male, female”

Query based on “class” = “class 1, class 2”

Query based on “grade” = “pass, fail”

By Traditional Design – All Detected

Analyzing the above detection requirements, there are 3 measured elements, which we call factors, and each factor has two values, which we call level values (that is, 2).

If it is an ordinary comprehensive inspection, it is as follows (2^3=8 times)
full verification
If it is an orthogonal method, it is as follows (2^2=4 times)
Orthogonal method
If the amount of input parameters is more, the benefit of the orthogonal method will be greater

Automatically generate orthogonal use cases

It is always cumbersome to manually list the input parameters. We can automatically output relevant combination scenarios through the orthogonal table. The rules of the orthogonal table are taken from
http://support.sas.com/techsup/technote/ts723_Designs.txt

# encoding: utf-8

from itertools import groupby
from collections import OrderedDict


def dataSplit(data):
    ds = []
    mb = [sum([k for m, k in data['mk'] if m <= 10]), sum([k for m, k in data['mk'] if m > 10] )]
    for i in data['data']:
        if mb[1] == 0:
            ds.append([int(d) for d in i])
        elif mb[0] == 0:
            ds.append([int(i[n * 2:(n + 1) * 2]) for n in range(mb[1])])
        else:
            part_1 = [int(j) for j in i[:mb[0]]]
            part_2 = [int(i[mb[0]:][n * 2:(n + 1) * 2]) for n in range(mb[1])]
            ds.append(part_1 + part_2)
    return ds


class OAT(object):
    def __init__(self, OAFile='./ts723_Designs.txt'):
        """
        Initialize, analyze and construct the orthogonal table object, data source: http://support.sas.com/techsup/technote/ts723_Designs.txt
        """
        self.data = {<!-- -->}

        # Parse the orthogonal table file data
        with open(OAFile, ) as f:
            # define temporary variables
            key = ''
            value = []
            pos = 0

            for i in f:
                i = i. strip()
                if 'n=' in i:
                    if key and value:
                        self.data[key] = dict(pos=pos,
                                              n=int(key. split('n=')[1]. strip()),
                                              mk=[[int(mk.split('^')[0]), int(mk.split('^')[1])] for mk in
                                                  key.split('n=')[0].strip().split(' ')],
                                              data=value)
                    key = ' '.join([k for k in i.split(' ') if k])
                    value = []
                    pos + = 1
                elif i:
                    value.append(i)

            self.data[key] = dict(pos=pos,
                                  n=int(key. split('n=')[1]. strip()),
                                  mk=[[int(mk.split('^')[0]), int(mk.split('^')[1])] for mk in
                                      key.split('n=')[0].strip().split(' ')],
                                  data=value)
        self.data = sorted(self.data.items(), key=lambda i: i[1]['pos'])

    def get(self, mk):
        """
        Incoming parameters: mk list, such as [(2,3)], [(5,5), (2,1)]

        1. Calculate m,n,k
        m=max(m1,m2,m3,…)
        k=(k1 + k2 + k3 + ...)
        n=k1*(m1-1) + k2*(m2-1) + …kx*x-1) + 1

       2. Query the orthogonal table
        Simple processing here, only return n minimum data satisfying >=m,n,k conditions, no complicated array inclusion check
        """
        mk = sorted(mk, key=lambda i: i[0])

        m = max([i[0] for i in mk])
        k = sum([i[1] for i in mk])
        n = sum([i[1] * (i[0] - 1) for i in mk]) + 1
        query_key = ' '.join(['^'.join([str(j) for j in i]) for i in mk])

        for data in self.data:
            # First check whether there is an exact matching orthogonal table data
            if query_key in data[0]:
                return dataSplit(data[1])
            #Otherwise, return n minimum data that satisfies >=m,n,k conditions
            elif data[1]['n'] >= n and data[1]['mk'][0][0] >= m and data[1]['mk'][0 ][1] >= k:
                return dataSplit(data[1])
        # no result
        return None

    def genSets(self, params, mode=0, num=1):
        """
        Pass in the test parameter OrderedDict, call the orthogonal table to generate the test set
        mode: use case clipping mode, value 0,1
            0 lenient mode, only crop the repeated test set
            1 Strict mode, in addition to cutting repeated test sets, also cut test sets containing None (num is the maximum number of test sets allowed for None)
        """
        sets = []

        # Sort by number of factor levels
        params = OrderedDict(sorted(params. items(), key=lambda x: len(x[1])))

        mk = [(k, len(list(v))) for k, v in groupby(params.items(), key=lambda x: len(x[1]))]
        data = self. get(mk)
        for d in data:
            # Generate a test set based on the results of the regular table
            q = OrderedDict()
            for index, (k, v) in zip(d, params.items()):
                try:
                    q[k] = v[index]
                except IndexError:
                    # When the value of the parameter is out of range, take None
                    q[k] = None
            if q not in sets:
                if mode == 0:
                    sets.append(q)
                elif mode == 1 and (len(list(filter(lambda v: v is None, q.values())))) <= num:
                    # Trim the test set, remove duplicates and test sets containing None
                    sets.append(q)
        return sets

Generate the above test case

if __name__ == "__main__":
    oat = OAT()
    case1 = OrderedDict([
        ("Gender: Male Female"]),
        ("Class", ["Class 1", "Class 2"]),
        ("Grade", ["Pass","Fail"])
    ])
    print(json. dumps(oat. genSets(case1), ensure_ascii=False))

The result is as follows

In this way, orthogonal use cases can be automatically generated

Sample code reference: https://github.com/lovesoo/OrthogonalArrayTest