Scripting Recipe – Oracles & Test Generation
An oracle for testing purposes can be a number of things: a person, a specification (like an RFC), a comparable product depending on what you are trying to do. If the oracle happens to be an algorithm then I tend to script it up in order to
- Have a separate implementation to use as a guide
- Be able to determine if the application is giving me the correct thing
- Create test data to use that meets the algorithm
The first 2 are scripting the algorithm up as an oracle instance and the third lets me not have to think about the validity of my test data letting me concentrate on more important parts of the testing effort.
Here is an example that I created to verify or generate an account number. In this scenario the usernames are not sequential, but instead are 9 digits long, cannot start with a 0, can only have numbers and the last digit is a mod-10 check digit. If run with an argument then it will verify the validity of the account number, otherwise it will create a valid one for you.
import random, sys, string
def verify_number(to_verify):
# length check
if len(to_verify) != 9:
# raise an exception with some context
msg = "Invalid account length"
raise SystemError, msg # yes, I know, SystemError isn't really the right error
# only numbers
for char in to_verify:
if char not in string.digits:
print "Accounts can only have numbers"
sys.exit(1)
# cant start with zero
if int(to_verify[0]) == 0:
print "Accounts cannot start with 0"
sys.exit(1)
# checkdigit
run_total = 0
for run in to_verify[:-1]:
run_total += int(run)
mod = run_total % 10
if int(to_verify[-1]) != mod:
print "Check-digit failed"
sys.exit(1)
print "Account is valid"
def generate_number():
account_num = []
account_num.append(str(random.randint(1,9)))
for ix in range(0,7):
account_num.append(str(random.randint(0,9)))
run_total = 0
for run in account_num:
run_total += int(run)
mod = run_total % 10
account_num.append(str(mod))
print "Your new account number is %s " % "".join(account_num)
if __name__ == "__main__":
if len(sys.argv) == 2:
# show a try/except block in a real example
try:
# verify provided number
verify_number(sys.argv[1])
except SystemError:
print caught
sys.exit(1)
elif len(sys.argv) == 1:
# generate number
generate_number()
else:
print "usage: my_script.py [account number]"
This happens to be in Python, but I once wrote a number of generators like this in Perl and had them all in a nicely dynamic CGI page which would give you account numbers for any available algorithm. It was pretty slick. I should try to find that code…