This repository provides a script for generating a starting point for automated tests using a YAML input file and Python code. The script generates pytest test functions for each test case specified in the YAML file. Cases for skipping and failing tests can be specified.

Installation

To use this script, you’ll need to install the required dependencies:

pip install -r requirements.txt

Usage

Generate pytest test cases from pytest_input.yaml

positional arguments:
  module_name  Name of the module to generate test cases for

optional arguments:
  -h, --help   show this help message and exit

To use the test generator, create a YAML file with test cases and pass the name of the Python module to test as a command line argument when running the pytest_maker.py script:

python pytest_maker.py my_module

This will generate a test_my_module.py file with pytest test functions.

Example

As an example, let’s say we have a module called my_module.py with the following functions:

from math import pi, sin
from random import randint

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    return x / y

def concat_str(a, b):
    return a + b

def concat_list(a, b):
    return a + b

def pi_multiply(a):
    return pi * a

YAML Input File

The pytest_input.yaml file is used to specify the test cases that will be generated by the test_maker.py script. Each test case is defined as a key-value pair in the format:

function_name$test_name:
  args: $value1$value2...$valueN
  equals: equals_output_value
  outtype: output_type
  skip: message_to_skip
  fail: message_to_fail

Where:

function_name is the name of the function to be tested. test_name is a unique identifier for the test case. args is a string that represents the arguments to be passed to the function, separated by a dollar sign ($). equals is the ‘==’ output of the function for the given arguments. more is the ‘>’ output of the function for the given arguments. moreoe is the ‘>=’ output of the function for the given arguments. less is the ‘<’ output of the function for the given arguments. lessoe is the ‘<=’ output of the function for the given arguments. eval_equals is the ‘==’ of the eval(output) of the function for the given arguments. eval_more is the ‘>’ eval(output) of the function for the given arguments. eval_moreoe is the ‘>=’ eval(output) of the function for the given arguments. eval_less is the ‘<’ eval(output) of the function for the given arguments. eval_lessoe is the ‘<=’ eval(output) of the function for the given arguments. outtype (optional) is the expected output data type. skip (optional) is a message to skip the test. fail (optional) is a message to indicate the test has failed. Note: only one of skip or fail can be used per test case.

It can also take the following format:

fixture$test_name:
  args: $value_to_return

to create custom fixtures:

@pytest.fixture
def test_fixture_test_name():
    return value_to_return

Here’s an example pytest_input.yaml file:

fixture$1:
  args: $['555']$['666']
fixture$2:
  args: $5
add$simple_add:
  skip: 'The function should be skipped'
  args: $2$3
  equals: 5
  outtype: int
subtract$simple_subtract:
  args: $3$3
  equals: 0
  less: 7
  outtype: int
multiply$1_to_7:
  fail: 'The value is not true'
  args: $1$7
  equals: 8
  more: 6
  outtype: int
pi_multiply$pi_times_5:
  args: $5
  eval_equals: 'pi*5'
  eval_lessoe: 'pi*10'
  outtype: float
pi_multiply$pi_times_math:
  args: $fixture_2
  eval_moreoe: 'randint(1, 4)'
  eval_lessoe: 'pi*sin(90)*20'
  outtype: float
concat_list$list:
  timeout: 3
  args: $[1]$[2]
  equals: [1, 2]
  outtype: List
concat_list$str:
  args: $fixture_1*
  equals: ['555', '666']
  outtype: List

Then, running python pytest_maker.py my_module will generate a file test_my_module.py with the following content which then prompts the user to run the tests created or it can be run using the pytest library:

import pytest
from math import pi
from math import sin
from random import randint

from typing import *
from boom import *


@pytest.fixture
def test_fixture_1():
    return ['555'], ['666']

@pytest.fixture
def test_fixture_2():
    return 5

@pytest.mark.skip(
    reason="The function should be skipped")
def test_add_simple_add() -> None:
    result = add(2, 3)
    assert isinstance(result, int)
    assert result == 5


def test_subtract_simple_subtract() -> None:
    result = subtract(3, 3)
    assert isinstance(result, int)
    assert result < 7


@pytest.mark.xfail(
    reason="The value is not true")
def test_multiply_1_to_7() -> None:
    result = multiply(1, 7)
    assert isinstance(result, int)
    assert result == 8
    assert result > 6


def test_pi_multiply_pi_times_5() -> None:
    result = pi_multiply(5)
    assert isinstance(result, float)
    assert result == pi*5
    assert result <= pi*10


def test_pi_multiply_pi_times_math(test_fixture_2) -> None:
    result = pi_multiply(test_fixture_2)
    assert isinstance(result, float)
    assert result <= pi*sin(90)*20
    assert result >= randint(1, 4)


@pytest.mark.timeout(3)
def test_concat_list_list() -> None:
    result = concat_list([1], [2])
    assert isinstance(result, List)
    assert result == [1, 2]


def test_concat_list_str(test_fixture_1) -> None:
    result = concat_list(*test_fixture_1)
    assert isinstance(result, List)
    assert result == ['555', '666']