В итоге я написал сценарий Python, чтобы делать больше или меньше того, что хотел.
#!/usr/bin/env python
import sys
import re
from optparse import OptionParser
from math import fabs
splitPattern = re.compile(r',|\s+|;')
class FailObject(object):
def __init__(self, options):
self.options = options
self.failure = False
def fail(self, brief, full = ""):
print ">>>> ", brief
if options.verbose and full != "":
print " ", full
self.failure = True
def exit(self):
if (self.failure):
print "FAILURE"
sys.exit(1)
else:
print "SUCCESS"
sys.exit(0)
def numSplit(line):
list = splitPattern.split(line)
if list[-1] == "":
del list[-1]
numList = [float(a) for a in list]
return numList
def softEquiv(ref, target, tolerance):
if (fabs(target - ref) <= fabs(ref) * tolerance):
return True
#if the reference number is zero, allow tolerance
if (ref == 0.0):
return (fabs(target) <= tolerance)
#if reference is non-zero and it failed the first test
return False
def compareStrings(f, options, expLine, actLine, lineNum):
### check that they're a bunch of numbers
try:
exp = numSplit(expLine)
act = numSplit(actLine)
except ValueError, e:
# print "It looks like line %d is made of strings (exp=%s, act=%s)." \
# % (lineNum, expLine, actLine)
if (expLine != actLine and options.checkText):
f.fail("Text did not match in line %d" % lineNum)
return
### check the ranges
if len(exp) != len(act):
f.fail("Wrong number of columns in line %d" % lineNum)
return
### soft equiv on each value
for col in range(0, len(exp)):
expVal = exp[col]
actVal = act[col]
if not softEquiv(expVal, actVal, options.tol):
f.fail("Non-equivalence in line %d, column %d"
% (lineNum, col))
return
def run(expectedFileName, actualFileName, options):
# message reporter
f = FailObject(options)
expected = open(expectedFileName)
actual = open(actualFileName)
lineNum = 0
while True:
lineNum += 1
expLine = expected.readline().rstrip()
actLine = actual.readline().rstrip()
## check that the files haven't ended,
# or that they ended at the same time
if expLine == "":
if actLine != "":
f.fail("Tested file ended too late.")
break
if actLine == "":
f.fail("Tested file ended too early.")
break
compareStrings(f, options, expLine, actLine, lineNum)
#print "%3d: %s|%s" % (lineNum, expLine[0:10], actLine[0:10])
f.exit()
################################################################################
if __name__ == '__main__':
parser = OptionParser(usage = "%prog [options] ExpectedFile NewFile")
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="Don't print status messages to stdout")
parser.add_option("--check-text",
action="store_true", dest="checkText", default=False,
help="Verify that lines of text match exactly")
parser.add_option("-t", "--tolerance",
action="store", type="float", dest="tol", default=1.e-15,
help="Relative error when comparing doubles")
(options, args) = parser.parse_args()
if len(args) != 2:
print "Usage: numdiff.py EXPECTED ACTUAL"
sys.exit(1)
run(args[0], args[1], options)
Большинство из того, что вы описываете, являются стандартными функциями модульного тестирования. Вы просите что-то выше и выше unittest? –
Единичное тестирование, насколько я понимаю, оно рассматривает минимальные тесты с априорными ответами. (Если я напишу функцию returnTwo, предназначенную для возврата значения два, я могу сделать единичный тест, чтобы проверить правильность возвращаемого значения.) Регрессионное тестирование, поскольку я имею в виду, что он должен генерировать данные на гораздо более высоком уровне и сравнивать данные, сгенерированные будущими версиями, с более старыми данными. –
Извините, ваше впечатление от регрессионного тестирования не очень полезно. Вы не сравниваете эту версию с предыдущей версией. Вы сравниваете обе версии с известными хорошими результатами. Всегда можно создать единичный тест, который имеет «магический» ответ, полученный из предыдущей версии, без объяснения причин.Это может служить проверкой того, что результаты предыдущей версии сохраняются, даже если никто не знает, правильны они или нет. –