Defining a Testing Framework
To improve readability and maintainability of tests we should separate test definition from the test framework, and create a reporting mechanism which is easy on the eyes and helps identify exactly where the error occurred. We can create a simple grammar that will define our tests centered around the fixture notion defined in previous section. Grammar for above test could look as follows.
fixture: LaunchForm
param:name=FormForTest
fixture: SetField
param:name=txt_from
param:value=abc
fixture: GetFieldValue
param:name = txt_to
expect: abc
fixture: ClickButton
param:name = btn_copy
fixture: GetFieldValue
param:name = txt_to
expect: abc
A test runner that would run a test written in this grammar and produce a simple html output is below. This runner makes the assumption that framework.py defines all of the relevant fixtures. This can be cleaned up a little bit by defining the notion of namespace for fixtures.
import argparse
import os, sys
from framework import *
from pyparsing import Literal, Word, Group, ZeroOrMore, CaselessLiteral,srange
from jinja2 import Template
class StepRunner:
def __init__(self, fixture, params, expect = None ):
self.fixture = fixture
self.expect = expect
self.params = dict([(k,v) for k,v in params])
self.formatted_params = ", ".join("%s=%s" % (k,v) for k,v in params)
self.result = "Not Run"
self.fail = False
def run(self):
runnable = globals()[self.fixture](**self.params)
try:
result = runnable.execute()
if result == self.expect:
if self.expect is None:
self.result = "PASS"
else:
self.result = result
else:
self.fail = True
self.result = "ERROR:%s != %s" % (self.expect, result)
except Exception as err:
self.fail = True
self.result = str(err)
def parse_file(file_name):
Identifier = Word(srange("[a-zA-Z0-9_]"))
fixture_line = (CaselessLiteral("fixture:").suppress() +Identifier)
param_line = CaselessLiteral("param:").suppress() + Identifier + Literal("=").suppress() +Identifier
expect_line = (CaselessLiteral("expect:").suppress() + Identifier )
fixture = fixture_line+ Group(ZeroOrMore(Group(param_line) )) + ZeroOrMore(expect_line)
grammar= ZeroOrMore(Group(fixture))
steps = []
with open(file_name) as f:
for fixture in grammar.parseString(f.read()):
steps.append(StepRunner( *fixture))
return steps
def format_result(file_name, steps):
template = Template("""<html>
<header> <center><h1> Test: {{test_name}} </h1></center> </header>
<body>
<table border="1" style="width:100%">
<tr bgcolor="#A8A8A8"><td>Fixture</td><td>Params</td><td>Result</td></tr>
{% for step in steps %}
{% if step.fail %}
<tr bgcolor="red">
{% else %}
<tr bgcolor="#00FF00">
{% endif %}
<td>{{step.fixture}}</td><td>{{step.formatted_params}}</td><td>{{step.result}}</td></tr>
{% endfor %}
</table></body></html>
""")
report_file_name = os.path.splitext(file_name)[0]+".html"
with open(report_file_name, "w") as f:
f.write(template.render(test_name=os.path.basename(file_name), steps=steps))
print ("Saved results to %s" % report_file_name)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Run a test.')
parser.add_argument('--file_name', dest='file_name', required = True)
args = parser.parse_args()
steps = parse_file( args.file_name)
for step in steps:
step.run()
format_result(args.file_name, steps)
if any(s.fail for s in steps):
print( "Test Failed")
sys.exit(1)
else:
print( "Test Completed Successfully")
sys.exit(0)
|
No comments:
Post a Comment