automated acceptance testing in plain english
DESCRIPTION
Using Concordion to write automated acceptance testsTRANSCRIPT
Acceptance TestingExecutable Requirements with Concordion
http://www.concordion.org
n. con-cord (kŏn'kôrd, kŏng–)
The tests are the spec, right?
• Source code is complicated
• Tests mix intent, implementation, and scaffold
• “the code” is meaningless to a customer
• “the code” is also meaningless to new developers
Requirement
When bad credentials are used to login, the response code should be 501, with a message of “Bad Password or Username”.
Pseudo Code test
def testLogin() { var username = “John” var password = “badpassword” var postMethod = new PostMethod(“http://localhost/login.do”) postMethod.setProperty(“username”, username) postMethod.setProperty(“password”, password) var return = new HttpClient().executeMethod(postMethod)
try { assertThat(return.responseCode).isEqualTo(501) assertThat(return.bodyText).isEqualTo(“Bad password or username.”) } finally { postMethod.releaseConnection() }}
So whats wrong?
• Hard coded values
• HTTP Client support code
• “the code” is meaningless to the customer
• “the code” is also meaningless to new developers (without a bit of groking)
ConcordionConcordion is an open source framework for Java that lets you turn a plain English description of a requirement into an automated test— an active specification.
• Developed by David Peterson in 2007
• Licensed under Apache v2.0
• Java Based
Identify Your Requirements?
• Abstract Parameters
• Common Actions
• Expected Results
Requirement
When bad credentials are used to login, the response code should be 501, with a message of “Bad Password or Username”.
HTML Markup
<p>When <span concordion:execute=”#credentials = getBadCredentials”>bad credentials</span> are used <span concordion:execute=”#result = tryLoginWith(#credentials)”>to login</span>, the response code should be <span concordion:assertEquals=”#result.responseCode”>501</span>, with a message of “<span concordion:assertEquals=”#result.bodyText”>Bad Password or Username</span>”.</p>
Pseudo Code Fixture
def getBadCredentials() = new Credentials(“John”,“badpassword”)
def tryLoginWith(cred: Credentials) = { var postMethod = new PostMethod(“http://localhost/login.do”) postMethod.setProperty(“username”, cred.username) postMethod.setProperty(“password”, cred.password) var return = new HttpClient().executeMethod(postMethod) return new Result(return.responseCode, return.bodyText);}
Result
When bad credentials are used to login, the response code should be 501, with a message of “Bad assword or UsernameBad Password or Username”.
There’s still something wrong!
• Fixture leaks state and code into specification
Stateless HTML Markup
<p>When <span concordion:execute=”useBadCredentials()”>bad credentials</span> are used <span concordion:execute=”tryLogin()”>to login</span>, the response code should be <span concordion:assertEquals=”getResponseCode()”>501</span>, with a message of “<span concordion:assertEquals=”getBodyText()”>Bad Password or Username</span>”.</p>
Stateless Pseudo Code Fixture
var cred = new Credentials(null, null)var result = new ReturnDetails(-1, “”)
def useBadCredentials() = { cred = new Credentials(“John”,“badpassword”)}
def tryLogin = { var postMethod = new PostMethod(“http://localhost/login.do”) postMethod.setProperty(“username”, cred.username) postMethod.setProperty(“password”, cred.password) var return = new HttpClient().executeMethod(postMethod)}
def getResponseCode() = result.responseCodedef getBodyText() = result.bodyText
Benefits
• Requirement doesn’t need to know HOW the requirement is exercised
• Implementation language could change without changing the specification.
• Pluggable specification fixtures( i.e. HTTP Client REST API and Selenium based WEB UI testing)
• Plain english requirement is easy to understand
More Information
• http://www.concordion.org
• http://tech.groups.yahoo.com/group/concordion/join
• http://www.talios.com
• http://www.twitter.com/talios