関数のテストをしてみる

こんばんわ。morimotoです。

前回の数値判定の関数を一括してテストするためにテストクラスを書いてみました。 少々汚いですが暴動とかデモとか起こさないでください。。。

まずはテストクラス function_test.py

# -*- coding: utf-8 -*-
class FunctionTest:
    def display_all(self, tests={}):
        for namespace in tests:
            print ""
            print "*" * 15 + (namespace + " Tests").center(30) + "*" * 15
            print ""
            cases = tests.get(namespace, [])
            for case in cases:
                functions = case.get('functions', [])
                args_list = case.get('args_list', [])
                args_default = case.get('args_default', {})
                self.display(namespace, functions, args_list, args_default)

    def display(self, namespace, functions, args_list, args_default={}):
        if functions:
            format, sep = self.get_format(functions[:], args_list[:])
            if args_default:
                print " * set args default values : %s" % ",".join([" %s = %s"%(name, args_default[name],) for name in args_default])
            print sep
            print format %1
            print sep
            for args in args_list:
                print format %2
            print sep
            print ''

    def get_format(self, functions, args_list):
        ph = lambda v: '%' + '%s'%v + 's'
        args_len = args_list and max([len(str(args)) for args in args_list[:]]) or 0
        func_lens = [len(str(func)) for func in functions[:]]
        format = '| ' + ph(args_len) + ' || ' + ' | '.join([ph(fl) for fl in func_lens[:]]) + ' |'
        sep = '+' + '-' * (args_len + 2) + '++' + '+'.join(['-' * (fl+2) for fl in func_lens[:]]) + '+'
        return format, sep

    def execute(self, namespace, function_name, args, args_default={}):
        my_module = __import__(namespace)
        my_func = getattr(my_module, function_name)
        return apply(my_func, self.args_to_tuple(args), self.args_to_dict(args, args_default))

    def args_to_tuple(self, args):
        if isinstance(args, tuple):
            return args
        if isinstance(args, list):
            return tuple(args)
        if isinstance(args, str):
            return tuple([args,])
        return tuple([])
    
    def args_to_dict(self, args, args_default={}):
        if isinstance(args, dict):
            args.update(args_default)
            return args
        return args_default

前回のutilities.py (try-catch でパースするのもテストしてみる)

# -*- coding: utf-8 -*-
import re

# Convert functions
def parseInt(value, default=None):
    (ret,) = validates_integer_of(value) and (int(value),) or \
            (validates_number_of(value) and (int(float(value)),) or (default,))
    return ret

def _parseInt(value, default=None):
    try:
        return int(value)
    except:
        return default

# Validate functions
def validates_number_of(value):
    return re.match(r'^(?![-+]0+$)[-+]?([1-9][0-9]*)?[0-9](\.[0-9]+)?$', '%s'%value) and True or False

def validates_integer_of(value):
    return re.match(r'^(?![-+]0*$)[-+]?([1-9][0-9]*)?0?$', '%s'%value) and True or False

def validates_float_of(value):
    return validates_number_of(value) and not validates_integer_of(value) or False

最後にテストするための test.py

# -*- coding: utf-8 -*-
from function_test import FunctionTest
from utilities import *

test_values = [
    "123456",
    "0",
    "-9",
    "+5",
    "+9",
    "0.0000",
    "1234.56",
    "-123.45",
    "+123.45",
    "-0.123",
    "123.00000",
    "+00000000",
    "-00000000",
    "+0",
    "-0",
    "ABCDEF",
    "1+23",
    "123-",
    "-.123",
    "123.",
    "00012",
    ".123",
    "+0000",
    "-0000",
    "+0000.22",
    "+",
    "-",
    ["12345"],
    ("12345",),
    {"value": "12345"},
]

test_functions = {
    'utilities': [
        {
            'functions': ['validates_number_of', 'validates_integer_of', 'validates_float_of', ],
            'args_list': test_values,
        },
        {
            'functions': ['parseInt', '_parseInt', ],
            'args_list': test_values,
            'args_default': { 'default': None, },
        },
   ]
}

ft = FunctionTest()
ft.display_all(test_functions)

  • test_functions の ‘utilities’ の部分は最初にimportしているテストしたい名前空間名。
  • 配列中の dictionary は引数が同じ(出来れば引数の数や名前が同じ)場合はまとめてしまって構わないです。
  • ‘functions’ は名前の通り関数名のリスト
  • ‘args_list’ はテストしたい引数のリスト。
  • ‘args_default’ は任意で値をセットしたい場合に引数名をキーに対して値を入れる。
  • test_values の下3つはテストクラスのテスト用(笑)
  • この3つのファイルを同じ階層に置いていただき、後はコンソールから

    python test.py
    

    を実行するだけです。

    結果

    >python test.py
    
    ***************       utilities Tests        ***************
    
    +--------------------++---------------------+----------------------+--------------------+
    |               args || validates_number_of | validates_integer_of | validates_float_of |
    +--------------------++---------------------+----------------------+--------------------+
    |             123456 ||                True |                 True |              False |
    |                  0 ||                True |                 True |              False |
    |                 -9 ||                True |                 True |              False |
    |                 +5 ||                True |                 True |              False |
    |                 +9 ||                True |                 True |              False |
    |             0.0000 ||                True |                False |               True |
    |            1234.56 ||                True |                False |               True |
    |            -123.45 ||                True |                False |               True |
    |            +123.45 ||                True |                False |               True |
    |             -0.123 ||                True |                False |               True |
    |          123.00000 ||                True |                False |               True |
    |          +00000000 ||               False |                False |              False |
    |          -00000000 ||               False |                False |              False |
    |                 +0 ||               False |                False |              False |
    |                 -0 ||               False |                False |              False |
    |             ABCDEF ||               False |                False |              False |
    |               1+23 ||               False |                False |              False |
    |               123- ||               False |                False |              False |
    |              -.123 ||               False |                False |              False |
    |               123. ||               False |                False |              False |
    |              00012 ||               False |                False |              False |
    |               .123 ||               False |                False |              False |
    |              +0000 ||               False |                False |              False |
    |              -0000 ||               False |                False |              False |
    |           +0000.22 ||               False |                False |              False |
    |                  + ||               False |                False |              False |
    |                  - ||               False |                False |              False |
    |          ['12345'] ||                True |                 True |              False |
    |         ('12345',) ||                True |                 True |              False |
    | {'value': '12345'} ||                True |                 True |              False |
    +--------------------++---------------------+----------------------+--------------------+
    
     * set args default values :  default = None
    +--------------------++----------+-----------+
    |               args || parseInt | _parseInt |
    +--------------------++----------+-----------+
    |             123456 ||   123456 |    123456 |
    |                  0 ||        0 |         0 |
    |                 -9 ||       -9 |        -9 |
    |                 +5 ||        5 |         5 |
    |                 +9 ||        9 |         9 |
    |             0.0000 ||        0 |      None |
    |            1234.56 ||     1234 |      None |
    |            -123.45 ||     -123 |      None |
    |            +123.45 ||      123 |      None |
    |             -0.123 ||        0 |      None |
    |          123.00000 ||      123 |      None |
    |          +00000000 ||     None |         0 |
    |          -00000000 ||     None |         0 |
    |                 +0 ||     None |         0 |
    |                 -0 ||     None |         0 |
    |             ABCDEF ||     None |      None |
    |               1+23 ||     None |      None |
    |               123- ||     None |      None |
    |              -.123 ||     None |      None |
    |               123. ||     None |      None |
    |              00012 ||     None |        12 |
    |               .123 ||     None |      None |
    |              +0000 ||     None |         0 |
    |              -0000 ||     None |         0 |
    |           +0000.22 ||     None |      None |
    |                  + ||     None |      None |
    |                  - ||     None |      None |
    |          ['12345'] ||    12345 |     12345 |
    |         ('12345',) ||    12345 |     12345 |
    | {'value': '12345'} ||    12345 |     12345 |
    +--------------------++----------+-----------+
    

    _parseInt のほうはボロボロでした。 int()よ、しっかりしてください・・・。

    以上morimotoでした。

    1. 'args',) + tuple(functions[:] []
    2. str(args),) + tuple([self.execute(namespace, func, args, args_default) for func in functions[:]] []
    Categorized: Python

    Comments are closed.