Software development
phmb@cin.ufpe.br ◈ twitter.com/pauloborba
Paulo Borba
Informatics Center
Federal University of Pernambuco
Testing: implementation, maintenance and
execution
Directly connecting requirements
(scenarios) and tests
Feature scenarios
Scenario: new article
Given the system has no article entitled
"A theory of software product line refinement"
When I create the article "A theory of software
product line refinement" with file name "TCS.pdf"
Then the article "A theory of software product line refinement" is properly stored by the system
Via controllers, given and when
Given(~'^the system has no article
entitled "([^"]*)"$') { String title ->
article = Periodico.findByTitle(title) assert article == null
}
When(~'^I create the article "([^"]*)" with file
name "([^"]*)"$') { String title, filename ->
TestDataAndOperations.createArticle(title, filename) }
Via controllers, then
Then(~'^the article "([^"]*)" is properly stored by the system$') { String title ->
article = Periodico.findByTitle(title)
assert TestDataAndOperations.compatibleTo(
article, title) }
Auxiliary classes
static public void createArticle(String title, filename) { def cont = new PeriodicoController()
cont.params << TestData.findArticleByTitle(title) << [file: filename]
cont.request.setContent(new byte[1000]) cont.create()
cont.save()
cont.response.reset() }
GUI based scenario
Scenario: new article web
Given I am at the publications menu When I select the "Article" option
And I select the new article option Then I can fill the article details
GUI based test, given
Given(~'^I am at the publications menu$') { ->
to LoginPage at LoginPage
page.add("admin","adminadmin") at PublicationsPage
}
GUI based test, when and then
When(~'^I select the "([^"]*)" option$') { String o ->
at PublicationsPage page.select(o)
}
Then(~'^I can fill the article details$') { ->
at ArticleCreatePage
page.fillArticleDetails() }
Pages, content
class ArticleCreatePage extends Page { static url = "periodico/create"
static at = { …
title ==~ gp.msg("default.create.label",…) journal != null
}
static content = { journal { $("input", id: "journal") } }
… }
Pages, functions
class ArticleCreatePage extends Page { …
def fillArticleDetails() {
$("form").title = "A theory of …"
$("form").journal = "Theoretical Computer …"…
} }
Pages, more functions
class PublicationsPage extends Page { …
def select(String s) {
$('div', id: 'status').find('a', text: s).click() }
def getLink(String linkName) {
$('div#status a', text: linkName) }
}
Practices
•
Parametrize tests•
Each test can run independently of the others, assuming a fresh application instance•
Passing state between steps•
Before pushing (sometimes committing), make sure all tests pass•
Executed 200 tests without a single error or failureCreate interface when little functionality is available
class PeriodicoController { create() {}
save() {}
}
functionality interface
Test behavior should strictly conform to scenario semantics
When(~'^I select to view "([^"]*)" in resulting list$') { String title ->
at ArticlesPage
page.selectViewArticle(title) at ArticleShowPage
} first and third command are
not implied by the scenario step semantics
Avoid ambiguities due to similar step sentences
Then(~'^I can fill the article details$') {->
at ArticleCreatePage
page.fillArticleDetails() }
Then(~'^I can fill the tool details$') { ->
at FerramentaCreatePage page.fillToolDetails()
}
Do not duplicate test code
def fillLoginDataOnly(...) {
$("form").username = username $("form").password = password }
def fillLoginDataAndSubmit(...) {
$("form").username = username $("form").password = password $("form").signIn().click()
} class ResearchGroupPage extends Page {
static url = "researchGroup/list" ...
}
class ResearchGroupListPage extends Page { static url = "researchGroup/list" ...
}
Tests should clean up environment at the end
After() { ...
def uploadsFolder = new File(...) uploadsFolder.listFiles().each { innerFile ->
innerFile.deleteOnExit() }
}
Do not mix GUI and controller tests:
they simulate different application instances
Given(~'^I am at the articles page and the article "([^"]*)"
is stored in the system with file name "([^"]*)"$') { String title, filename ->
...
TestDataAndOperations.createArticle(title, filename) article = Periodico.findByTitle(title)
assert article != null to ArticlesPage
at ArticlesPage }
Tests should be plataform (including browser) and language independent
class ArticleCreatePage extends Page { static url = "article/create"
static at = {
def gp = new GetPageTitle()
def a = gp.msg("...article.label")
def t = gp.msg("...create.label", [a]) title ==~ t
...
Software development
phmb@cin.ufpe.br ◈ twitter.com/pauloborba
Paulo Borba
Informatics Center
Federal University of Pernambuco