Acceptance test spike example

Steve Jorgensen nospam at nospam.nospam
Sun Jun 26 19:10:05 EDT 2005


I'm posting this message for 2 reasons.

First, I'm still pretty new and shakey to the whole Acceptance Testing thing,
and I'm hoping for some feedback on whether I'm on the right track.  Second,
although all the Agile literature talks about the importance of doing
Acceptance Testing, there's very little in any of the books or out on the Web
that helps with how to do it.  If I am on the right track, this will be one
more helpful item folks can find on a Google search.

The code below is basically a spike I wrote in a few hours last night to prove
to myself and my team that it would be feasible to quickly create a simple,
useful Acceptance Testing harness for our learning project.

First, I wrote an example test script I would hope to be able to execute...

===============
?recipeListCount:0
!new
?name:"New Recipe"
name:"PB&J"
?name:"PB&J"
!save
!close
?recipeListCount:1
!goToListItem 1
!openListItem
?name:"PB&J"
===============

In this script, ? means test a value, ! means execute an action, and a line
that starts with neither ? or ! is a value assignment.

Next, here's the Python code I wrote to try to run the script.  This was just
proof of concept, so at this point, it just runs a single test script with the
script file name hard coded.  Also, for simplicty, the skeleton of the
hypothetical application model object is, for now, in-line in the AT system
code.

===============
import string


class RecipeOrgModel:
    recipeListCount = 0
    name = "New Recipe"

    def new(self):
        pass

    def save(self):
        pass

    def close(self):
        pass

    def goToListItem(self,itemNum):
        pass

    def openListItem(self):
        pass


class ParsedStatement:
    name = None
    value = ""

    def __init__(self,statementText,nameValuesDelim):
        delimPos = string.find(statementText,nameValuesDelim)
        if delimPos==-1:
            self.name = statementText
        else:
            self.name = statementText[0:delimPos]
            self.value = statementText[delimPos+1:]


class TestSession:
    fileName=""
    lines = None
    model = RecipeOrgModel()
    
    def __init__(self,fileName):
        self.fileName = fileName
    
    def run(self):
        self.loadLines(self.fileName)

        for line in self.lines:
            if not self.processLine(line): break

    def loadLines(self, fileName):
        file = open(fileName)
        lines = file.readlines()
        file.close
        
        lines = map(string.strip,lines)
        self.lines = lines

    def processLine(self,line):
        print(line)
        
        if line[0]=='?':
            return(
                self.TestValue(line[1:])
                )
        elif line[0]=='!':
            self.DoAction(line[1:])
            return 1
        else:
            self.AssignValue(line)
            return 1

    def TestValue(self,statement):
        parsed = ParsedStatement(statement,":")
        valueExpr = "self.model." + parsed.name
        valueIs = eval(valueExpr)
        valueExpected = eval(parsed.value)
        hasExpectedVal = (valueExpected == valueIs)
        if not hasExpectedVal:
            print("  - Expected " + str(parsed.value) + ", but got " +
str(valueIs))
        return(hasExpectedVal)

    def AssignValue(self,statement):
        parsed = ParsedStatement(statement,":")
        exec("self.model." + parsed.name + "=" + parsed.value)

    def DoAction(self,statement):
        parsed = ParsedStatement(statement," ")
        methodCall = "self.model." + parsed.name +"(" + parsed.value + ")"
        exec(methodCall)


session = TestSession("test1.txt")
session.run()
===============

Note how the powerful, context-aware exec() and eval() procedures really help
simplify the code.

Finally, here's the output I get when I run this with the example script above
saved in a file called test1.txt in the current directory.

===============
?recipeListCount:0
!new
?name:"New Recipe"
name:"PB&J"
?name:"PB&J"
!save
!close
?recipeListCount:1
  - Expected 1, but got 0
===============

And this result is pretty much what we expect to see - cool.



More information about the Python-list mailing list