Developer Testing Tools Roundup
 

Developer Testing Tools Roundup

on

  • 5,184 views

An overview of testing tools and techniques available to Java developers

An overview of testing tools and techniques available to Java developers

Statistics

Views

Total Views
5,184
Views on SlideShare
5,134
Embed Views
50

Actions

Likes
26
Downloads
231
Comments
0

6 Embeds 50

http://paper.li 29
http://blog.fasoulas.com 9
http://a0.twimg.com 8
http://us-w1.rockmelt.com 2
http://www.m.techgig.com 1
http://safe.txmblr.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Developer Testing Tools Roundup Developer Testing Tools Roundup Presentation Transcript

  • AgendaWhat will we cover today How to name your tests Hamcrest Matchers Parameterized tests JUnit Rules Mockito Spock Geb Web testing Thucydides and easyb
  • Most importantly...Practice Test Driven Development
  • What’s in a nameName your tests well "Whats in a name? That which we call a rose By any other name would smell as sweet." Romeo and Juliet (II, ii, 1-2)
  • What’s in a nameThe 10 5 Commandments of Test WritingI. Don’t say “test, say “should” insteadII. Don’t test your classes, test their behaviourIII. Test class names are important tooIV. Structure your tests wellV. Tests are deliverables too
  • What’s in a nameDon’t use the word ‘test’ in your test names testBankTransfer() testWithdraw() testDeposit()
  • What’s in a name Do use the word ‘should’ in your test names testBankTransfer() testWithdraw()tranferShouldDeductSumFromSourceAccountBalance() testDeposit()transferShouldAddSumLessFeesToDestinationAccountBalance()depositShouldAddAmountToAccountBalance()
  • What’s in a nameYour test class names should represent context When is this behaviour applicable? What behaviour are we testing?
  • What’s in a nameWrite your tests consistently ‘Given-When-Then’ or ‘Arrange-Act-Assert’ (AAA)@Testpublic void aDeadCellWithOneLiveNeighbourShouldRemainDeadInTheNextGeneration() { String initialGrid = "...n" + ".*.n" + Prepare the test data (“arrange”) "..."; String expectedNextGrid = "...n" + "...n" + "...n"; Do what you are testing (“act”) Universe theUniverse = new Universe(seededWith(initialGrid)); theUniverse.createNextGeneration(); String nextGrid = theUniverse.getGrid(); Check the results (“assert”) assertThat(nextGrid, is(expectedNextGrid));}
  • What’s in a nameTests are deliverables too - respect them as suchRefactor, refactor, refactor!Clean and readable
  • Express Yourself with HamcrestWhy write this... import static org.junit.Assert.*; ... assertEquals(10000, calculatedTax, 0);when you can write this... import static org.hamcrest.Matchers.*; ... assertThat(calculatedTax, is(10000)); “Assert that are equal 10000 and calculated tax (more or less)” ?! Don’t I just mean “assert that calculated tax is 10000”?
  • Express Yourself with HamcrestWith Hamcrest, you can have your cake and eat it! assertThat(calculatedTax, is(expectedTax)); Readable asserts String color = "red"; assertThat(color, is("blue")); Informative errors String[] colors = new String[] {"red","green","blue"}; String color = "yellow"; assertThat(color, not(isIn(colors))); Flexible notation
  • Express Yourself with HamcrestMore Hamcrest expressiveness String color = "red"; assertThat(color, isOneOf("red",”blue”,”green”)); List<String> colors = new ArrayList<String>(); colors.add("red"); colors.add("green"); colors.add("blue"); assertThat(colors, hasItem("blue")); assertThat(colors, hasItems("red”,”green”)); assertThat(colors, hasItem(anyOf(is("red"), is("green"), is("blue"))));
  • Home-made Hamcrest MatchersCustomizing and extending HamcrestCombine existing matchersOr make your own!
  • Home-made Hamcrest MatchersCustomizing Hamcrest matchers You can build your own by combining existing Matchers... Create a dedicated Matcher for the Stakeholder classList stakeholders = stakeholderManager.findByName("Health");Matcher<Stakeholder> calledHealthCorp = hasProperty("name", is("Health Corp"));assertThat(stakeholders, hasItem(calledHealthCorp)); Use matcher directly with hasItem() “The stakeholders list has (at least) one item with the name property set to “Health Corp””
  • Home-made Hamcrest Matchers Writing your own matchers in three easy steps!public class WhenIUseMyCustomHamcrestMatchers { @Test public void thehasSizeMatcherShouldMatchACollectionWithExpectedSize() { List<String> items = new ArrayList<String>(); items.add("java"); assertThat(items, hasSize(1)); }} We want something like this... I want a matcher that checks the size of a collection
  • Home-made Hamcrest MatchersWriting your own matchers in three easy steps!public class HasSizeMatcher extends TypeSafeMatcher<Collection<? extends Object>> { private Matcher<Integer> matcher; Extend the TypeSafeMatcher class public HasSizeMatcher(Matcher<Integer> matcher) { this.matcher = matcher; Provide expected values in } the constructor public boolean matchesSafely(Collection<? extends Object> collection) { return matcher.matches(collection.size()); } Do the actual matching public void describeTo(Description description) { description.appendText("a collection with a size that is"); matcher.describeTo(description); } Describe our expectations} So let’s write this Matcher!
  • Home-made Hamcrest Matchers Writing your own matchers in three easy steps!import java.util.Collection;import org.hamcrest.Factory;import org.hamcrest.Matcher;public class MyMatchers { Use a factory class to store your matchers @Factory public static Matcher<Collection<? extends Object>> hasSize(Matcher<Integer> matcher){ return new HasSizeMatcher(matcher); }} All my custom matchers go in a special Factory class
  • Home-made Hamcrest Matchers Writing your own matchers in three easy steps!import static com.wakaleo.gameoflife.hamcrest.MyMatchers.hasSize;import static org.hamcrest.MatcherAssert.assertThat;public class WhenIUseMyCustomHamcrestMatchers { @Test public void thehasSizeMatcherShouldMatchACollectionWithExpectedSize() { List<String> items = new ArrayList<String>(); items.add("java"); assertThat(items, hasSize(1)); }} Hamcrest-style error messages
  • Home-made Hamcrest Matchers But wait! There’s more! @Test public void weCanUseCustomMatchersWithOtherMatchers() { List<String> items = new ArrayList<String>(); items.add("java"); assertThat(items, allOf(hasSize(1), hasItem("java"))); } Combining matchers @Test public void weCanUseCustomMatchersWithOtherMatchers() { List<String> items = new ArrayList<String>(); items.add("java"); items.add("groovy"); assertThat(items, hasSize(greaterThan(1))); } Nested matchers
  • Data-Driven Unit TestsUsing Parameterized Tests
  • Using Parameterized TestsParameterized tests - for data-driven testingTake a large set of test data, including an expected resultDefine a test that uses the test dataVerify calculated result against expected result {2, 0, 0} {2, 1, 2} {2, 2, 4} {2, 3, 6} {2, 4, 8} x=a*b {2, 5, 10} Test {2, 6, 12} {2, 7, 14} ... Verify Data
  • Using Parameterized TestsParameterized testsExample: Calculating income tax
  • Using Parameterized Tests Parameterized tests with JUnit 4.8.1 Income Expected Tax $0.00 $0.00 What you need: $10,000.00 $1,250.00 Some test data $14,000.00 $1,750.00 $14,001.00 $1,750.21 A test class with matching fields $45,000.00 $8,260.00 $48,000.00 $8,890.00 And some tests $48,001.00 $8,890.33 $65,238.00 $14,578.54 And an annotation $70,000.00 $16,150.00public class TaxCalculatorDataTest {@RunWith(Parameterized.class) $70,001.00 $16,150.38public classdouble income; private TaxCalculatorDataTest { $80,000.00 $19,950.00 private double expectedTax; income; private double expectedTax; $100,000.00 $27,550.00 public TaxCalculatorDataTest(double income, double expectedTax) { super(); this.income = income; public TaxCalculatorDataTest(double income, double expectedTax) { this.income = income; this.expectedTax = expectedTax; super(); } this.expectedTax = expectedTax; this.income = income;} } this.expectedTax = expectedTax; } @Test public void shouldCalculateCorrectTax() {...} @Test} public void shouldCalculateCorrectTax() {...}}
  • Using Parameterized TestsHow it works This is a parameterized test Income Expected Tax@RunWith(Parameterized.class) $0.00 $0.00public class TaxCalculatorDataTest { The @Parameters annotation $10,000.00 $1,250.00 private double income; private double expectedTax; indicates the test data $14,000.00 $1,750.00 @Parameters $14,001.00 $1,750.21 public static Collection<Object[]> data() { $45,000.00 $8,260.00 return Arrays.asList(new Object[][] { { 0.00, 0.00 }, $48,000.00 $8,890.00 { 10000.00, 1250.00 }, { 14000.00, 1750.00 }, $48,001.00 $8,890.33 { 14001.00, 1750.21 }, { 45000.00, 8260.00 }, { 48000.00, 8890.00 }, { 48001.00, 8890.33 }, $65,238.00 $14,578.54 { 65238.00, 14578.54 }, { 70000.00, 16150.00 }, $70,000.00 $16,150.00 { 70001.00, 16150.38 }, { 80000.00, 19950.00 }, { 100000.00, 27550.00 }, }); $70,001.00 $16,150.38 } $80,000.00 $19,950.00 public TaxCalculatorDataTest(double income, double expectedTax) { $100,000.00 $27,550.00 super(); this.income = income; this.expectedTax = expectedTax; The constructor takes the } fields from the test data @Test public void shouldCalculateCorrectTax() { TaxCalculator calculator = new TaxCalculator(); The unit tests use data double calculatedTax = calculator.calculateTax(income); assertThat(calculatedTax, is(expectedTax)); from these fields. }}
  • Using Parameterized TestsParameterized Tests in Eclipse Income Expected TaxRun the test only once $0.00 $0.00 $10,000.00 $1,250.00Eclipse displays a result for each data set $14,000.00 $1,750.00 $14,001.00 $1,750.21 $45,000.00 $8,260.00 $48,000.00 $8,890.00 $48,001.00 $8,890.33 $65,238.00 $14,578.54 $70,000.00 $16,150.00 $70,001.00 $16,150.38 $80,000.00 $19,950.00 $100,000.00 $27,550.00
  • Using Parameterized TestsExample: using an Excel Spreadsheet @Parameters public static Collection spreadsheetData() throws IOException { InputStream spreadsheet = new FileInputStream("src/test/resources/aTimesB.xls"); return new SpreadsheetData(spreadsheet).getData(); }
  • JUnit RulesUsing Existing and Custom JUnit RulesCustomize and control how JUnit behaves
  • JUnit Rules The Temporary Folder Rulepublic class LoadDynamicPropertiesTest { Create a temporary folder @Rule public TemporaryFolder folder = new TemporaryFolder(); private File properties; @Before Prepare some test data public void createTestData() throws IOException { properties = folder.newFile("messages.properties"); BufferedWriter out = new BufferedWriter(new FileWriter(properties)); // Set up the temporary file out.close(); } Use this folder in the tests @Test public void shouldLoadFromPropertiesFile() throws IOException { DynamicMessagesBundle bundle = new DynamicMessagesBundle(); bundle.load(properties); // Do stuff with the temporary file }} The folder will be deleted afterwards
  • JUnit Rules The ErrorCollector Rule Report on multiple error conditions in a single testpublic class ErrorCollectorTest { @Rule public ErrorCollector collector = new ErrorCollector(); @Test Two things went wrong here public void testSomething() { collector.addError(new Throwable("first thing went wrong")); collector.addError(new Throwable("second thing went wrong")); String result = doStuff(); collector.checkThat(result, not(containsString("Oh no, not again"))); } Check using Hamcrest matchers private String doStuff() { return "Oh no, not again"; }}
  • JUnit Rules The ErrorCollector Rule Report on multiple error conditions in a single testpublic class ErrorCollectorTest { @Rule All three error messages are reported public ErrorCollector collector = new ErrorCollector(); @Test public void testSomething() { collector.addError(new Throwable("first thing went wrong")); collector.addError(new Throwable("second thing went wrong")); String result = doStuff(); collector.checkThat(result, not(containsString("Oh no, not again"))); } private String doStuff() { return "Oh no, not again"; }}
  • JUnit Rules The Timeout Rule Define a timeout for all testspublic class GlobalTimeoutTest { @Rule public MethodRule globalTimeout = new Timeout(1000); @Test No test should take longer than 1 second public void testSomething() { for(;;); } Oops @Test public void testSomethingElse() { }}
  • Parallel tests Setting up parallel tests with JUnit and Maven<project...> Needs Surefire 2.5 <plugins> ... <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.5</version> <configuration> ‘methods’, ‘classes’, or ‘both’ <parallel>methods</parallel> </configuration> </plugin> </plugins> ... <build> <dependencies> <dependency> Needs JUnit 4.8.1 or better <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> </dependencies> </build> ...</project>
  • Continuous TestingContinuous Tests with InfinitestInfinitest is a continuous test tool for Eclipse and IntelliJRuns your tests in the background when you save your code
  • Continuous TestingUsing Infinitest Whenever you save your file changes, unit tests will be rerun Failing testProject containing an error Error message about the failed test
  • Mocking with styleMockito - lightweight Java mocking Account balance number getFees(feeType) import static org.mockito.Mockito.*; .... Account accountStub = mock(Account.class); when(accountStub.getFees(FeeType.ACCOUNT_MAINTENANCE)).thenReturn(4.00); when(accountStub.getFees(FeeType.TRANSACTION_FEE)).thenReturn(0.50); assertThat(accountStub.getFees(FeeType.TRANSACTION_FEE), is(0.50)); Low-formality mocking
  • Mocking with styleMockito - lightweight Java mocking AccountDaocreateNewAccount(String id) AccountDao accountDao = mock(AccountDao.class); Account newAccount = mock(Account.class); when(accountDao.createNewAccount(123456)).thenReturn(newAccount) .thenThrow(new AccountExistsException() ); Manage successive calls
  • Mocking with styleMockito - lightweight Java mocking AccountDaocreateNewAccount(String id) @Mock private AccountDao accountDao ... Mockito annotations
  • Mocking with styleMockito - lightweight Java mocking Account balance number getEarnedInterest(year)when(accountStub.getEarnedInterest(intThat(greaterThan(2000)))).thenReturn(10.00); Use matchers
  • Mocking with styleMockito - lightweight Java mocking AccountDaocreateNewAccount(String id) @Mock private AccountDao accountDao ... // Test stuff ... verify(accountDao).createNewAccount( (String) isNotNull()); Verify interactions
  • Spock - Unit BDD in GroovySpecifications in Groovyimport spock.lang.Specification; Specifications, not testsclass RomanCalculatorSpec extends Specification { def "I plus I should equal II"() { given: def calculator = new RomanCalculator() when: def result = calculator.add("I", "I") then: result == "II" }}
  • Spock - Unit BDD in GroovySpecifications in Groovy BDD-styledef "I plus I should equal II"() { when: "I add two roman numbers together" def result = calculator.add("I", "I") then: "the result should be the roman number equivalent of their sum" result == "II" }
  • Spock - Unit BDD in GroovySpecifications in Groovy BDD-styledef "I plus I should equal II"() { when: "I add two roman numbers together" def result = calculator.add("I", "I") then: "the result should be the roman number equivalent of their sum" result == "II" } This is the assert I plus I should equal II(com.wakaleo.training.spocktutorial.RomanCalculatorSpec) Time elapsed: 0.33 sec <<< FAILURE! Condition not satisfied: result == "II" | | I false 1 difference (50% similarity) I(-) I(I) at com.wakaleo.training.spocktutorial .RomanCalculatorSpec.I plus I should equal II(RomanCalculatorSpec.groovy:17)
  • Spock - Unit BDD in GroovySpecifications in Groovydef "The lowest number should go at the end"() { when: def result = calculator.add(a, b) then: result == sum Data-driven testing where: a | b | sum "X" | "I" | "XI" "I" | "X" | "XI" "XX" | "I" | "XXI" "XX" | "II" | "XXII" "II" | "XX" | "XXII" }
  • Spock - Unit BDD in GroovySpecifications in Groovydef "Messages published by the publisher should only be received by active subscribers"() { given: "a publisher" def publisher = new Publisher() and: "some active subscribers" Subscriber activeSubscriber1 = Mock() Setting up mocks Subscriber activeSubscriber2 = Mock() activeSubscriber1.isActive() >> true activeSubscriber2.isActive() >> true publisher.add activeSubscriber1 publisher.add activeSubscriber2 and: "a deactivated subscriber" Subscriber deactivatedSubscriber = Mock() deactivatedSubscriber.isActive() >> false publisher.add deactivatedSubscriber when: "a message is published" publisher.publishMessage("Hi there") Asserts on mocks then: "the active subscribers should get the message" 1 * activeSubscriber1.receive("Hi there") 1 * activeSubscriber2.receive({ it.contains "Hi" }) and: "the deactivated subscriber didnt receive anything" 0 * deactivatedSubscriber.receive(_)}
  • Geb - Groovy Page ObjectsDSL for WebDriver web testingimport geb.*  Browser.drive("http://google.com/ncr") { Concise expression language  assert title == "Google"  // enter wikipedia into the search field  $("input", name: "q").value("wikipedia")  // wait for the change to results page to happen  // (google updates the page without a new request)  waitFor { title.endsWith("Google Search") }  // is the first link to wikipedia? Higher level than WebDriver  def firstLink = $("li.g", 0).find("a.l")  assert firstLink.text() == "Wikipedia"  // click the link  firstLink.click() Power asserts  // wait for Googles javascript to redirect  // us to Wikipedia  waitFor { title == "Wikipedia" }}
  • ATDDor Specification by example The story of your app
  • User storiesAs a job seekerI want to find jobs in relevant categoriesSo that I can find a suitable job Features/Epics
  • User storiesAs a job seekerI want to find jobs in relevant categoriesSo that I can find a suitable job Acceptance criteria ☑  The  job  seeker  can  see  available  categories  on  the  home  page ☑  The  job  seeker  can  look  for  jobs  in  a  given  category ☑  The  job  seeker  can  see  what  category  a  job  belongs  to
  • User stories As a job seeker I want to find jobs in relevant categories So that I can find a suitable job Acceptance criteria ☑  The  job  seeker  can  see  available  categories  on  the  home  page ☑  The  job  seeker  can  look  for  jobs  in  a  given  category ☑  The  job  seeker  can  see  what  category  a  job  belongs  toscenario "A job seeker can see the available job categories on the home page",{ when "the job seeker is looking for a job", then "the job seeker can see all the available job categories"} Automated acceptance test
  • scenario "A job seeker can see the available job categories on the home page", { when "the job seeker is looking for a job", then "the job seeker can see all the available job categories" } Automated acceptance testImplemented development tests Implemented acceptance tests
  • The art of sustainable web tests or how not to have web tests like this
  • The Three Ways of Automated Web Testing Record/Replay ScriptingPage Objects
  • Record-replay automated tests Promise Reality
  • Record-replay automated tests
  • Script-based automated testsSelenium RC HTMLUnit JWebUnit Canoe Webtest Watir
  • Script-based automated testsSelenium RC HTMLUnit JWebUnit Canoe Webtest Watir
  • How about Page Objects?Reusable Low maintenance Hide unnecessary detail 2
  • A sample Page Object A web page
  • A sample Page Object A Page Object FindAJobPage lookForJobsWithKeywords(values : String) getJobTitles() : List<String>
  • A sample Page Object public class FindAJobPage extends PageObject { An implemented WebElement keywords; WebElement searchButton; Page Object public FindAJobPage(WebDriver driver) { super(driver); } public void lookForJobsWithKeywords(String values) { typeInto(keywords, values); searchButton.click(); } public List<String> getJobTitles() { List<WebElement> tabs = getDriver() .findElements(By.xpath("//div[@id=jobs]//a")); return extract(tabs, on(WebElement.class).getText()); } }
  • A sample Page Object public class WhenSearchingForAJob { @Test public void searching_for_a_job_should_display_matching_jobs() { FindAJobPage page = new FindAJobPage(); page.open("http://localhost:9000"); page.lookForJobsWithKeywords("Java"); assertThat(page.getJobTitles(), hasItem("Java Developer")); } } A test using this Page Object
  • Sustainable web tests Are we there yet?
  • Acceptance TestsThe high-level view So  where  are   we  at?
  • Page Objects Page  Objects   rock! Implementation focus
  • How do we bridge the gap?
  • How do we bridge the gap? Test steps
  • scenario "A job seeker can see the available job categories on the home page", { when "the job seeker is looking for a job", then "the job seeker can see all the available job categories" } Automatedscenario "The user can see the available job categories on the home page",{ when "the job seeker is looking for a job", { job_seeker.open_jobs_page() } then "the job seeker can see all the available job categories", { job_seeker.should_see_job_categories "Java Developers", "Groovy Developers" }} Implemented JobSeekerSteps JobSeekerSteps JobSeekerSteps open_jobs_page() open_jobs_page() open_jobs_page() should_see_job_categories(String...  categories) should_see_job_categories(String...  categories) ... should_see_job_categories(String...  categories) ... ... Step libraries
  • scenario "The user can see the available job categories on the home page",{ when "the job seeker is looking for a job", { job_seeker.open_jobs_page() } then "the job seeker can see all the available job categories", { job_seeker.should_see_job_categories "Java Developers", "Groovy Developers" }} Implemented Tests JobSeekerSteps JobSeekerSteps JobSeekerSteps open_jobs_page() open_jobs_page() open_jobs_page() should_see_job_categories(String...  categories) should_see_job_categories(String...  categories) ... should_see_job_categories(String...  categories) ... ... Step libraries Page Objects
  • Test steps help organize your tests
  • Test stepsare a communication tool
  • Test steps are reusable building blocks
  • Test stepshelp estimate progress
  • And so we built a tool...
  • Webdriver/Selenium 2 extensionOrganize tests, stories and features Record/report test execution Measure functional coverage
  • Thucydides in action A simple demo app
  • Defining your acceptance tests scenario "A job seeker { can see the available j ob categori when "the j es on the h ob seeker i ome page", then "the j s looking f ob seeker c o r a j o b ", } an see all the availab le job cate gories" High level requ irements... scenario "The administrator adds a new category to the system", { given "a new category needs to be added to the system", when "the administrator adds a new category", then "the system should confirm that the category has been created", and "the new category should be visible to job seekers", } { scenario "The admini strator deletes a ca tegory from the syst em", ...defined in business terms given "a category ne eds to be deleted", when "the administra tor deletes a catego then "the system will ry", confirm that the cate and "the deleted cate gory has been delete gory should no longer d", } be visible to job se eker s", focus on business value
  • Organizing your requirements Features public class Application { @Feature public class ManageCompanies { public class AddNewCompany {} public class DeleteCompany {} public class ListCompanies {} } @Feature public class ManageCategories { public class AddNewCategory {} public class ListCategories {} public class DeleteCategory {} } @Feature Stories public class BrowseJobs { public class UserLookForJobs {} public class UserBrowsesJobTabs {} } }
  • Implementing your acceptance testsusing "thucydides" We are testing this storythucydides.uses_steps_from AdministratorStepsthucydides.uses_steps_from JobSeekerStepsthucydides.tests_story AddNewCategory An acceptance criteriascenario "The administrator adds a new category to the system",{ given "a new category needs to be added to the system", { Narrative style administrator.logs_in_to_admin_page_if_first_time() administrator.opens_categories_list() } Step through an when "the administrator adds a new category", example { administrator.selects_add_category() administrator.adds_new_category("Scala Developers","SCALA") } then "the system should confirm that the category has been created", { administrator.should_see_confirmation_message "The Category has been created" } and "the new category should be visible to job seekers", { Still high-level job_seeker.opens_jobs_page() job_seeker.should_see_job_category "Scala Developers" }}
  • Some folks prefer JUnit...@RunWith(ThucydidesRunner.class)@Story(AddNewCategory.class) Thucydides handles thepublic class AddCategoryStory { web driver instances @Managed public WebDriver webdriver; @ManagedPages(defaultUrl = "http://localhost:9000") public Pages pages; @Steps public AdministratorSteps administrator; @Steps Using the same steps public JobSeekerSteps job_seeker; @Test public void administrator_adds_a_new_category_to_the_system() { administrator.logs_in_to_admin_page_if_first_time(); administrator.opens_categories_list(); administrator.selects_add_category(); administrator.adds_new_category("Java Developers","JAVA"); administrator.should_see_confirmation_message("The Category has been created"); job_seeker.opens_job_page(); job_seeker.should_see_job_category("Java Developers"); } Tests can be pending @Pending @Test public void administrator_adds_an_existing_category_to_the_system() {}}
  • Defining your test stepspublic class AdministratorSteps extends ScenarioSteps { A step library @Step public void opens_categories_list() { AdminHomePage page = getPages().get(AdminHomePage.class); page.open(); page.selectObjectType("Categories"); } High level steps... @Step public void selects_add_category() { CategoriesPage categoriesPage = getPages().get(CategoriesPage.class); categoriesPage.selectAddCategory(); } @Step public void adds_new_category(String label, String code) { EditCategoryPage newCategoryPage = getPages().get(EditCategoryPage.class); newCategoryPage.saveNewCategory(label, code); } ...implemented @Step public void should_see_confirmation_message(String message) { with Page Objects AdminPage page = getPages().get(AdminPage.class); page.shouldContainConfirmationMessage(message); } @StepGroup ...or with other steps public void deletes_category(String name) { opens_categories_list(); displays_category_details_for(name); deletes_category(); }}
  • Defining your page objectspublic class EditCategoryPage extends PageObject { @FindBy(id="object_label") WebElement label; Provides some useful @FindBy(id="object_code") utility methods... WebElement code; @FindBy(name="_save") WebElement saveButton; public EditCategoryPage(WebDriver driver) { super(driver); } public void saveNewCategory(String labelValue, String codeValue) { typeInto(label, labelValue); typeInto(code, codeValue); saveButton.click(); }} but otherwise a normal WebDriver Page Object
  • Data-driven testing Test data categories.csvpublic class DataDrivenCategorySteps extends ScenarioSteps { Test steps public DataDrivenCategorySteps(Pages pages) { super(pages); } private String name; private String code; @Steps public AdminSteps adminSteps; public void setCode(String code) {...} public void setName(String name) {...} @Step public void add_a_category() { adminSteps.add_category(name, code); }}
  • Data-driven testing Test data categories.csvpublic class DataDrivenCategorySteps extends ScenarioSteps { Test steps public DataDrivenCategorySteps(Pages pages) { super(pages); } private String name; private String code; @Steps public AdminSteps adminSteps; @Steps public void setCode(String code) {...} public DataDrivenCategorySteps categorySteps; public void setName(String name) {...} @Step @Test public void add_a_category() { Call this step for public void adding_multiple_categories() throws IOException { adminSteps.add_category(name, code); steps.login_to_admin_page_if_first_time();} } steps.open_categories_list(); each row withTestDataFrom("categories.csv").run(categorySteps).add_a_category(); }
  • Now run your tests
  • Displaying the results in easyb
  • Displaying the results in easyb
  • Thucydides reports Browse the features
  • Browse the stories Browse the stories
  • Browse the stories Browse the test scenarios
  • Illustrating the test paths A test scenario Steps Test scenarios
  • Illustrating the test paths Test scenario Steps Test scenarios Screenshots
  • Functional coverage Features
  • Functional coverage User stories
  • Functional coverage Passing tests Failing tests Pending tests
  • Functional coverage Test scenarios
  • Functional coverage
  • Thank you! John Ferguson Smart Email: john.smart@wakaleo.com Web: http://www.wakaleo.com Twitter: wakaleo