AgendaWhat will we cover today How to name your tests Hamcrest Matchers Parameterized tests JUnit Rules Mockito Spock Geb ...
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....
What’s in a nameThe 10 5 Commandments of Test WritingI.   Don’t say “test, say “should” insteadII. Don’t test your classes...
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()tranferShouldDeduc...
What’s in a nameYour test class names should represent context                         When is this behaviour applicable? ...
What’s in a nameWrite your tests consistently    ‘Given-When-Then’ or ‘Arrange-Act-Assert’ (AAA)@Testpublic void aDeadCell...
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...
Express Yourself with HamcrestWith Hamcrest, you can have your cake and eat it!  assertThat(calculatedTax, is(expectedTax)...
Express Yourself with HamcrestMore Hamcrest expressiveness String color = "red"; assertThat(color, isOneOf("red",”blue”,”g...
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...         ...
Home-made Hamcrest Matchers Writing your own matchers in three easy steps!public class WhenIUseMyCustomHamcrestMatchers {	...
Home-made Hamcrest MatchersWriting your own matchers in three easy steps!public class HasSizeMatcher extends TypeSafeMatch...
Home-made Hamcrest Matchers   Writing your own matchers in three easy steps!import java.util.Collection;import org.hamcres...
Home-made Hamcrest Matchers    Writing your own matchers in three easy steps!import static com.wakaleo.gameoflife.hamcrest...
Home-made Hamcrest Matchers    But wait! There’s more!	   @Test	   public void weCanUseCustomMatchersWithOtherMatchers() {...
Data-Driven Unit TestsUsing Parameterized Tests
Using Parameterized TestsParameterized tests - for data-driven testingTake a large set of test data, including an expected...
Using Parameterized TestsParameterized testsExample: Calculating income tax
Using Parameterized Tests Parameterized tests with JUnit 4.8.1                                       Income     Expected T...
Using Parameterized TestsHow it works                                 This is a parameterized test                        ...
Using Parameterized TestsParameterized Tests in Eclipse                                                  Income     Expect...
Using Parameterized TestsExample: using an Excel Spreadsheet   @Parameters   public static Collection spreadsheetData() th...
JUnit RulesUsing Existing and Custom JUnit RulesCustomize and control how JUnit behaves
JUnit Rules    The Temporary Folder Rulepublic class LoadDynamicPropertiesTest {                                          ...
JUnit Rules    The ErrorCollector Rule        Report on multiple error conditions in a single testpublic class ErrorCollec...
JUnit Rules    The ErrorCollector Rule        Report on multiple error conditions in a single testpublic class ErrorCollec...
JUnit Rules    The Timeout Rule     Define a timeout for all testspublic class GlobalTimeoutTest {    	 @Rule    	 public M...
Parallel tests Setting up parallel tests with JUnit and Maven<project...>                                  Needs Surefire ...
Continuous TestingContinuous Tests with InfinitestInfinitest is a continuous test tool for Eclipse and IntelliJRuns your tes...
Continuous TestingUsing Infinitest  Whenever you save your file changes, unit tests will be rerun                           ...
Mocking with styleMockito - lightweight Java mocking      Account  balance  number  getFees(feeType)        import static ...
Mocking with styleMockito - lightweight Java mocking       AccountDaocreateNewAccount(String id)  AccountDao accountDao = ...
Mocking with styleMockito - lightweight Java mocking       AccountDaocreateNewAccount(String id)   @Mock private AccountDa...
Mocking with styleMockito - lightweight Java mocking         Account balance number getEarnedInterest(year)when(accountStu...
Mocking with styleMockito - lightweight Java mocking       AccountDaocreateNewAccount(String id)   @Mock private AccountDa...
Spock - Unit BDD in GroovySpecifications in Groovyimport spock.lang.Specification;             Specifications, not testscla...
Spock - Unit BDD in GroovySpecifications in Groovy                             BDD-styledef "I plus I should equal II"() { ...
Spock - Unit BDD in GroovySpecifications in Groovy                                   BDD-styledef "I plus I should equal II...
Spock - Unit BDD in GroovySpecifications in Groovydef "The lowest number should go at the end"() {        when:            ...
Spock - Unit BDD in GroovySpecifications in Groovydef "Messages published by the publisher should only be received by activ...
Geb - Groovy Page ObjectsDSL for WebDriver web testingimport geb.*  Browser.drive("http://google.com/ncr") {        Concis...
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                      ...
User storiesAs a job seekerI want to find jobs in relevant categoriesSo that I can find a suitable job                      ...
User stories    As a job seeker    I want to find jobs in relevant categories    So that I can find a suitable job          ...
scenario "A job seeker can see the available job categories on the home page", { 	   when "the job seeker is looking for a...
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             ...
Script-based automated testsSelenium RC      HTMLUnit              JWebUnit                     Canoe Webtest             ...
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    ...
A sample Page Object  public class FindAJobPage extends PageObject {                                                      ...
A sample Page Object  public class WhenSearchingForAJob {      @Test      public void searching_for_a_job_should_display_m...
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...
scenario "The user can see the available job categories on the home page",{	   when "the job seeker is looking for a job",...
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 execut...
Thucydides in action   A simple demo app
Defining your acceptance tests  scenario "A              job seeker  {                       can see the                   ...
Organizing your requirements Features   public class Application {                @Feature                public class Man...
Implementing your acceptance testsusing "thucydides"                                   We are testing this storythucydides...
Some folks prefer JUnit...@RunWith(ThucydidesRunner.class)@Story(AddNewCategory.class)                      Thucydides han...
Defining your test stepspublic class AdministratorSteps extends ScenarioSteps {                  A step library    @Step   ...
Defining your page objectspublic class EditCategoryPage extends PageObject {    @FindBy(id="object_label")    WebElement la...
Data-driven testing                                                Test data             categories.csvpublic class DataDr...
Data-driven testing                                                Test data             categories.csvpublic class DataDr...
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          ...
Illustrating the test paths                                           Test                                         scenari...
Functional coverage                      Features
Functional coverage                       User                      stories
Functional coverage                      Passing tests                              Failing tests                         ...
Functional coverage                        Test                      scenarios
Functional coverage
Thank you!         John Ferguson Smart         Email: john.smart@wakaleo.com          Web: http://www.wakaleo.com         ...
Developer Testing Tools Roundup
Developer Testing Tools Roundup
Upcoming SlideShare
Loading in...5
×

Developer Testing Tools Roundup

5,087

Published on

An overview of testing tools and techniques available to Java developers

Published in: Technology, Education
0 Comments
28 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
5,087
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
237
Comments
0
Likes
28
Embeds 0
No embeds

No notes for slide

Developer Testing Tools Roundup

  1. 1. AgendaWhat will we cover today How to name your tests Hamcrest Matchers Parameterized tests JUnit Rules Mockito Spock Geb Web testing Thucydides and easyb
  2. 2. Most importantly...Practice Test Driven Development
  3. 3. 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)
  4. 4. 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
  5. 5. What’s in a nameDon’t use the word ‘test’ in your test names testBankTransfer() testWithdraw() testDeposit()
  6. 6. What’s in a name Do use the word ‘should’ in your test names testBankTransfer() testWithdraw()tranferShouldDeductSumFromSourceAccountBalance() testDeposit()transferShouldAddSumLessFeesToDestinationAccountBalance()depositShouldAddAmountToAccountBalance()
  7. 7. What’s in a nameYour test class names should represent context When is this behaviour applicable? What behaviour are we testing?
  8. 8. 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));}
  9. 9. What’s in a nameTests are deliverables too - respect them as suchRefactor, refactor, refactor!Clean and readable
  10. 10. 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”?
  11. 11. 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
  12. 12. 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"))));
  13. 13. Home-made Hamcrest MatchersCustomizing and extending HamcrestCombine existing matchersOr make your own!
  14. 14. 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””
  15. 15. 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
  16. 16. 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!
  17. 17. 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
  18. 18. 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
  19. 19. 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
  20. 20. Data-Driven Unit TestsUsing Parameterized Tests
  21. 21. 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
  22. 22. Using Parameterized TestsParameterized testsExample: Calculating income tax
  23. 23. 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() {...}}
  24. 24. 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. }}
  25. 25. 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
  26. 26. 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(); }
  27. 27. JUnit RulesUsing Existing and Custom JUnit RulesCustomize and control how JUnit behaves
  28. 28. 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
  29. 29. 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"; }}
  30. 30. 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"; }}
  31. 31. 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() { }}
  32. 32. 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>
  33. 33. Continuous TestingContinuous Tests with InfinitestInfinitest is a continuous test tool for Eclipse and IntelliJRuns your tests in the background when you save your code
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. Mocking with styleMockito - lightweight Java mocking AccountDaocreateNewAccount(String id) @Mock private AccountDao accountDao ... Mockito annotations
  38. 38. Mocking with styleMockito - lightweight Java mocking Account balance number getEarnedInterest(year)when(accountStub.getEarnedInterest(intThat(greaterThan(2000)))).thenReturn(10.00); Use matchers
  39. 39. Mocking with styleMockito - lightweight Java mocking AccountDaocreateNewAccount(String id) @Mock private AccountDao accountDao ... // Test stuff ... verify(accountDao).createNewAccount( (String) isNotNull()); Verify interactions
  40. 40. 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" }}
  41. 41. 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" }
  42. 42. 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)
  43. 43. 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" }
  44. 44. 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(_)}
  45. 45. 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" }}
  46. 46. ATDDor Specification by example The story of your app
  47. 47. User storiesAs a job seekerI want to find jobs in relevant categoriesSo that I can find a suitable job Features/Epics
  48. 48. 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
  49. 49. 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
  50. 50. 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
  51. 51. The art of sustainable web tests or how not to have web tests like this
  52. 52. The Three Ways of Automated Web Testing Record/Replay ScriptingPage Objects
  53. 53. Record-replay automated tests Promise Reality
  54. 54. Record-replay automated tests
  55. 55. Script-based automated testsSelenium RC HTMLUnit JWebUnit Canoe Webtest Watir
  56. 56. Script-based automated testsSelenium RC HTMLUnit JWebUnit Canoe Webtest Watir
  57. 57. How about Page Objects?Reusable Low maintenance Hide unnecessary detail 2
  58. 58. A sample Page Object A web page
  59. 59. A sample Page Object A Page Object FindAJobPage lookForJobsWithKeywords(values : String) getJobTitles() : List<String>
  60. 60. 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()); } }
  61. 61. 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
  62. 62. Sustainable web tests Are we there yet?
  63. 63. Acceptance TestsThe high-level view So  where  are   we  at?
  64. 64. Page Objects Page  Objects   rock! Implementation focus
  65. 65. How do we bridge the gap?
  66. 66. How do we bridge the gap? Test steps
  67. 67. 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
  68. 68. 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
  69. 69. Test steps help organize your tests
  70. 70. Test stepsare a communication tool
  71. 71. Test steps are reusable building blocks
  72. 72. Test stepshelp estimate progress
  73. 73. And so we built a tool...
  74. 74. Webdriver/Selenium 2 extensionOrganize tests, stories and features Record/report test execution Measure functional coverage
  75. 75. Thucydides in action A simple demo app
  76. 76. 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
  77. 77. 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 {} } }
  78. 78. 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" }}
  79. 79. 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() {}}
  80. 80. 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(); }}
  81. 81. 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
  82. 82. 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); }}
  83. 83. 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(); }
  84. 84. Now run your tests
  85. 85. Displaying the results in easyb
  86. 86. Displaying the results in easyb
  87. 87. Thucydides reports Browse the features
  88. 88. Browse the stories Browse the stories
  89. 89. Browse the stories Browse the test scenarios
  90. 90. Illustrating the test paths A test scenario Steps Test scenarios
  91. 91. Illustrating the test paths Test scenario Steps Test scenarios Screenshots
  92. 92. Functional coverage Features
  93. 93. Functional coverage User stories
  94. 94. Functional coverage Passing tests Failing tests Pending tests
  95. 95. Functional coverage Test scenarios
  96. 96. Functional coverage
  97. 97. Thank you! John Ferguson Smart Email: john.smart@wakaleo.com Web: http://www.wakaleo.com Twitter: wakaleo
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×